Statement of the business task

Assessing consumer usage trends of smart devices to inform Bellabeat marketing strategy

Data sources used

FitBit Fitness Tracker Data was downloaded from Kaggle (FitBit Fitness Tracker Data (kaggle.com)) and stored on my laptop in two sub-folders. In my analysis, I used relevant tables/dataframes to analyze relationships between total steps, calories, activity minutes, time in bed and sleep time. There are many dataframes with most of them repeated using different measures for the same thing.The dailyActivity_merged dataframe has the advantage of measuring many things in it. Therefore, that table was retained. The minuteSleep_merged dataframe was also added. All dataframes had the word merged with no distinction or specific meaning. So it was left as it was. The dataframes used are in long format.

Data integrity was ensured by following steps.

Reliable?

Data was provided by public data source (Kaggle), it was assumed reliable, but was not verified. Original: the data was provided voluntarily by the people who used the smart devices for a certain period.

Comprehensive

The data used covers the aspect of the business task under analysis.

Current

The data is a static data for a fixed period.

Complete

completeness was verified during data integrity check.

Data cleaning and transformation

The data cleaning follows the installation and loading the necessary packages.

The function sample() was used to check for data bias.

library(ggplot2)
library(tidyverse)
library(janitor)
library(lubridate)
library(dplyr)
library(reshape2)
library(tidyr)
library(SimDesign)
dailyactivity <- read.csv("C:/Users/nokwanda/Documents/dailyactivity.csv")
minutesleep <- read.csv("C:/Users/nokwanda/Documents/minutesleep.csv")
missing_data_count <- colSums(is.na(dailyactivity))
sum(duplicated(dailyactivity))
sum(duplicated(minutesleep))

There are no missing data in the 2 dataframes. However, because the minuteSleep dataframe has 543 duplicates, it was dropped and replaced with sleepDay_merged dataframe with only 3 duplicates that have been removed, and the dataframe renamed to sleepDay_merged_unique.

sleepday   <- read.csv("C:/Users/nokwanda/Documents/sleepday.csv")
missing_data_count <- colSums(is.na(sleepday))
sum(duplicated(sleepday))
sleepdayunique <- sleepday %>% 
unique()
sum(duplicated(sleepdayunique))

The sleepDay_merged_unique dataframe date (sleepDay) has been transformed into date format to be the sames as the other dataframes transformed date formats.

All dates and/or time in the 2 dataframes were formatted and changed to a uniform name: activityDate, and columns renamed as/if necessary.

Bias

After this data cleaning and transformation process, I assume that any possible bias in the data has been substantially reduced to a minimum.

Insufficient data

Because there is no time to collect new data, and extra data is not available, the analysis will be based on the existing data only.

Data analysis

Summary statistics about each dataframe

For the daily activity dataframe:

dailyactivity %>%  
  select(TotalSteps,  SedentaryMinutes, FairlyActiveMinutes, VeryActiveMinutes,
         Calories) %>%
  summary()
   TotalSteps    SedentaryMinutes FairlyActiveMinutes VeryActiveMinutes    Calories   
 Min.   :    0   Min.   :   0.0   Min.   :  0.00      Min.   :  0.00    Min.   :   0  
 1st Qu.: 3790   1st Qu.: 729.8   1st Qu.:  0.00      1st Qu.:  0.00    1st Qu.:1828  
 Median : 7406   Median :1057.5   Median :  6.00      Median :  4.00    Median :2134  
 Mean   : 7638   Mean   : 991.2   Mean   : 13.56      Mean   : 21.16    Mean   :2304  
 3rd Qu.:10727   3rd Qu.:1229.5   3rd Qu.: 19.00      3rd Qu.: 32.00    3rd Qu.:2793  
 Max.   :36019   Max.   :1440.0   Max.   :143.00      Max.   :210.00    Max.   :4900  

For the sleepday unique dataframe:

sleepdayunique %>%  
  select(TotalSleepRecords,
         TotalMinutesAsleep,
         TotalTimeInBed) %>%
  summary()
 TotalSleepRecords TotalMinutesAsleep TotalTimeInBed 
 Min.   :1.00      Min.   : 58.0      Min.   : 61.0  
 1st Qu.:1.00      1st Qu.:361.0      1st Qu.:403.8  
 Median :1.00      Median :432.5      Median :463.0  
 Mean   :1.12      Mean   :419.2      Mean   :458.5  
 3rd Qu.:1.00      3rd Qu.:490.0      3rd Qu.:526.0  
 Max.   :3.00      Max.   :796.0      Max.   :961.0  

Exploring some plots

Let see what these summaries tell us about how this sample of people’s activities, starting with the relationship between total steps taken and calories used.

ggplot(dailyactivity) + 
  geom_smooth(mapping = aes(x=TotalSteps, y=Calories)) +
  labs(title = 'Total steps taken vs calories burned')
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

This plot shows that the more steps are taken, the more calories are used/lost/burned, although it seems to slow down when it start getting to very high number of steps taken. This will help direct the marketing to customers who would be looking to burn more calories for any reason such as wanting to lose weight, or those who lost appetite and want to retrieve it again, so that they can start walking more. The marketing team could also use this info to advertise to athletes who would want to measure the steps they take a day against the calories they would need.

Let see how is the relationship between total minutes asleep and total time in bed.

ggplot(data = sleepdayunique) + geom_smooth(mapping = aes(x=TotalMinutesAsleep, y=TotalTimeInBed )) + labs(title = 'Total minutes asleep vs total minutes in bed')
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

This graph is not linear as one would expect, showing that the time in bed is not equal to the time asleep. This means that further exploration is needed before making a conclusion here.

Let’s now merge these two data sets.

combineddata <- merge(dailyactivity, sleepdayunique, by="Id")
head(combineddata)

Now, let’s look at the relationship between steps taken and sleep time.

ggplot(data = combineddata, mapping = aes(x=TotalSteps, y=TotalMinutesAsleep))+ geom_smooth() + labs(title = 'Total steps taken vs total minutes asleep')
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

There is no clear relationship between these two variable, for instance indicating that the more steps, the more or less sleep time, as it seems to vary with the line graph starting high, then going down and up again. This info may help the marketing team to draw attention of the customers to the fact that taking more steps will not necessary make them sleep more. An advice on the average steps may be recommended.

Let’s also look at the relationship between Very active minutes and minutes asleep.

ggplot(data = combineddata, mapping = aes(x=VeryActiveMinutes, y=TotalMinutesAsleep)) + geom_smooth() +
  labs(title = 'Very active minutes vs total minutes asleep')
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

Here too, there is no clear conclusion to be drawn since the relationship fluctuates a lot, showing a high asleep time at zero minutes activity, then very low sleep time at around 40 minutes activity, and going high again at around an hour activity, and down again.

Let’s now look at what is happening between sedentary minutes and total minutes asleep.

ggplot(data = combineddata, mapping = aes(x=SedentaryMinutes, y=TotalMinutesAsleep)) + geom_smooth() +
  labs(title = 'Sedentary minutes vs total minutes asleep')
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

Here again, there is no clear conclusion to be drawn since the relationship fluctuates a lot, showing a relatively high asleep time at zero sedentary minutes to a highest asleep time around 400 sedentary minutes, then down very low sleep time at around 1150 sedentary minutes, and going up again.

Finally, let see what is happening in between very active minutes and sedentary minutes by looking at fairly active minutes against total minutes asleep.

ggplot(data = combineddata, mapping = aes(x=FairlyActiveMinutes, y=TotalMinutesAsleep)) + geom_smooth() +
  labs(title = 'Fairly active minutes vs total minutes asleep')
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

This graph shows a better relationship, even though not perfect, that low to fairly active minutes result in longer sleep time. It can be noticed that the sleep time gradually decreases as active minutes increase before starting to pick up slightly again after 100 of active minutes. From these last three graphs analysis, the marketing team is able to advise customers on how active is enough for a good sleep, and recommend an average number of minutes of activities.

In conclusion, analysing these activities and visualizing them gives a good idea to the Bellabeat marketingn team on the strategies to adopt for their advertsing campaign. Asking more of their clients to regularly report data on an online portal or requesting to connect the various apps and to the company’s server to allow the creation of dashboards to monitor the tools use should be considered for better decision making.

LS0tDQp0aXRsZTogIkZpdEJpdCBBbmFseXNpcyINCmF1dGhvcjogIkJvdWJhIElzbWFpbGEiDQpkYXRlOiAiMjAyNC0wNS0yNCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgU3RhdGVtZW50IG9mIHRoZSBidXNpbmVzcyB0YXNrDQoNCkFzc2Vzc2luZyBjb25zdW1lciB1c2FnZSB0cmVuZHMgb2Ygc21hcnQgZGV2aWNlcyB0byBpbmZvcm0gQmVsbGFiZWF0IG1hcmtldGluZyBzdHJhdGVneQ0KDQojIERhdGEgc291cmNlcyB1c2VkDQoNCkZpdEJpdCBGaXRuZXNzIFRyYWNrZXIgRGF0YSB3YXMgZG93bmxvYWRlZCBmcm9tIEthZ2dsZSAoRml0Qml0IEZpdG5lc3MgVHJhY2tlciBEYXRhIChrYWdnbGUuY29tKSkgYW5kIHN0b3JlZCBvbiBteSBsYXB0b3AgaW4gdHdvIHN1Yi1mb2xkZXJzLiBJbiBteSBhbmFseXNpcywgSSB1c2VkIHJlbGV2YW50IHRhYmxlcy9kYXRhZnJhbWVzIHRvIGFuYWx5emUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRvdGFsIHN0ZXBzLCBjYWxvcmllcywgYWN0aXZpdHkgbWludXRlcywgdGltZSBpbiBiZWQgYW5kIHNsZWVwIHRpbWUuIFRoZXJlIGFyZSBtYW55IGRhdGFmcmFtZXMgd2l0aCBtb3N0IG9mIHRoZW0gcmVwZWF0ZWQgdXNpbmcgZGlmZmVyZW50IG1lYXN1cmVzIGZvciB0aGUgc2FtZSB0aGluZy5UaGUgZGFpbHlBY3Rpdml0eV9tZXJnZWQgZGF0YWZyYW1lIGhhcyB0aGUgYWR2YW50YWdlIG9mIG1lYXN1cmluZyBtYW55IHRoaW5ncyBpbiBpdC4gVGhlcmVmb3JlLCB0aGF0IHRhYmxlIHdhcyByZXRhaW5lZC4gVGhlIG1pbnV0ZVNsZWVwX21lcmdlZCBkYXRhZnJhbWUgd2FzIGFsc28gYWRkZWQuIEFsbCBkYXRhZnJhbWVzIGhhZCB0aGUgd29yZCBtZXJnZWQgd2l0aCBubyBkaXN0aW5jdGlvbiBvciBzcGVjaWZpYyBtZWFuaW5nLiBTbyBpdCB3YXMgbGVmdCBhcyBpdCB3YXMuDQpUaGUgZGF0YWZyYW1lcyB1c2VkIGFyZSBpbiBsb25nIGZvcm1hdC4NCg0KIyBEYXRhIGludGVncml0eSB3YXMgZW5zdXJlZCBieSBmb2xsb3dpbmcgc3RlcHMuDQoNCiogQWxsIERhdGVzIHN0YXRlZCBpbiBkaWZmZXJlbnQgZm9ybXMgd2VyZSBmb3JtYXR0ZWQgdG8gdXNlIG9uZSBjb21tb24gZm9ybWF0czogTU0uREQuWVlZWQ0KRGF0YSB3YXMgdmVyaWZpZWQgZm9yIGNvbXBsZXRlbmVzcyB0byBtYWtlIHN1cmUgdGhhdCBubyBkYXRhIHdhcyBsZWZ0IG91dCBkdXJpbmcgcmVwbGljYXRpb24gKGNvcHlpbmcgb3IgaW1wb3J0aW5nKS4NCiogRGF0ZSBmaWVsZHMgd2VyZSBhbHNvIGZvcm1hdHRlZCB0byBiZSBkYXRlIG5vdCBzdHJpbmcvY2hhcmFjdGVyLg0KV2hlbiBtYW5pcHVsYXRpbmcgZGF0YSBkdXJpbmcgY2xlYW5pbmcsIGNhcmVmdWwgYXR0ZW50aW9uIHdhcyBleGVyY2lzZWQgbm90IHRvIHJlbW92ZSBkYXRhIGp1c3QgYmVjYXVzZSBpdCBhcHBlYXJzIHRvIGJlIGEgZHVwbGljYXRlLCBidXQgYWZ0ZXIgdmVyaWZpY2F0aW9uLg0KQ3JlZGliaWxpdHkgb2YgZGF0YQ0KDQojIFJlbGlhYmxlPyANCg0KRGF0YSB3YXMgcHJvdmlkZWQgYnkgcHVibGljIGRhdGEgc291cmNlIChLYWdnbGUpLCBpdCB3YXMgYXNzdW1lZCByZWxpYWJsZSwgYnV0IHdhcyBub3QgdmVyaWZpZWQuDQpPcmlnaW5hbDogdGhlIGRhdGEgd2FzIHByb3ZpZGVkIHZvbHVudGFyaWx5IGJ5IHRoZSBwZW9wbGUgd2hvIHVzZWQgdGhlIHNtYXJ0IGRldmljZXMgZm9yIGEgY2VydGFpbiBwZXJpb2QuDQoNCiMgQ29tcHJlaGVuc2l2ZSANCg0KVGhlIGRhdGEgdXNlZCBjb3ZlcnMgdGhlIGFzcGVjdCBvZiB0aGUgYnVzaW5lc3MgdGFzayB1bmRlciBhbmFseXNpcy4NCg0KIyBDdXJyZW50DQoNClRoZSBkYXRhIGlzIGEgc3RhdGljIGRhdGEgZm9yIGEgZml4ZWQgcGVyaW9kLiANCg0KIyBDb21wbGV0ZSANCg0KY29tcGxldGVuZXNzIHdhcyB2ZXJpZmllZCBkdXJpbmcgZGF0YSBpbnRlZ3JpdHkgY2hlY2suDQoNCiMgRGF0YSBjbGVhbmluZyBhbmQgdHJhbnNmb3JtYXRpb24NCg0KVGhlIGRhdGEgY2xlYW5pbmcgZm9sbG93cyB0aGUgaW5zdGFsbGF0aW9uIGFuZCBsb2FkaW5nIHRoZSBuZWNlc3NhcnkgcGFja2FnZXMuDQoNClRoZSBmdW5jdGlvbiBzYW1wbGUoKSB3YXMgdXNlZCB0byBjaGVjayBmb3IgZGF0YSBiaWFzLg0KDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGphbml0b3IpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoU2ltRGVzaWduKQ0KYGBgDQoNCg0KYGBge3J9DQpkYWlseWFjdGl2aXR5IDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9ub2t3YW5kYS9Eb2N1bWVudHMvZGFpbHlhY3Rpdml0eS5jc3YiKQ0KYGBgDQpgYGB7cn0NCm1pbnV0ZXNsZWVwIDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9ub2t3YW5kYS9Eb2N1bWVudHMvbWludXRlc2xlZXAuY3N2IikNCmBgYA0KDQoNCmBgYHtyfQ0KbWlzc2luZ19kYXRhX2NvdW50IDwtIGNvbFN1bXMoaXMubmEoZGFpbHlhY3Rpdml0eSkpDQpgYGANCg0KYGBge3J9DQpzdW0oZHVwbGljYXRlZChkYWlseWFjdGl2aXR5KSkNCmBgYA0KYGBge3J9DQpzdW0oZHVwbGljYXRlZChtaW51dGVzbGVlcCkpDQpgYGANCg0KVGhlcmUgYXJlIG5vIG1pc3NpbmcgZGF0YSBpbiB0aGUgMiBkYXRhZnJhbWVzLiBIb3dldmVyLCBiZWNhdXNlIHRoZSBtaW51dGVTbGVlcCBkYXRhZnJhbWUgaGFzIDU0MyBkdXBsaWNhdGVzLCBpdCB3YXMgZHJvcHBlZCBhbmQgcmVwbGFjZWQgd2l0aCBzbGVlcERheV9tZXJnZWQgZGF0YWZyYW1lIHdpdGggb25seSAzIGR1cGxpY2F0ZXMgdGhhdCBoYXZlIGJlZW4gcmVtb3ZlZCwgYW5kIHRoZSBkYXRhZnJhbWUgcmVuYW1lZCB0byBzbGVlcERheV9tZXJnZWRfdW5pcXVlLg0KDQpgYGB7cn0NCnNsZWVwZGF5ICAgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL25va3dhbmRhL0RvY3VtZW50cy9zbGVlcGRheS5jc3YiKQ0KbWlzc2luZ19kYXRhX2NvdW50IDwtIGNvbFN1bXMoaXMubmEoc2xlZXBkYXkpKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtKGR1cGxpY2F0ZWQoc2xlZXBkYXkpKQ0KYGBgDQpgYGB7cn0NCnNsZWVwZGF5dW5pcXVlIDwtIHNsZWVwZGF5ICU+JSANCnVuaXF1ZSgpDQpzdW0oZHVwbGljYXRlZChzbGVlcGRheXVuaXF1ZSkpDQpgYGANCg0KVGhlIHNsZWVwRGF5X21lcmdlZF91bmlxdWUgZGF0YWZyYW1lIGRhdGUgKHNsZWVwRGF5KSBoYXMgYmVlbiB0cmFuc2Zvcm1lZCBpbnRvIGRhdGUgZm9ybWF0IHRvIGJlIHRoZSBzYW1lcyBhcyB0aGUgb3RoZXIgZGF0YWZyYW1lcyB0cmFuc2Zvcm1lZCBkYXRlIGZvcm1hdHMuDQoNCkFsbCBkYXRlcyBhbmQvb3IgdGltZSBpbiB0aGUgMiBkYXRhZnJhbWVzIHdlcmUgZm9ybWF0dGVkIGFuZCBjaGFuZ2VkIHRvIGEgdW5pZm9ybSBuYW1lOiBhY3Rpdml0eURhdGUsIGFuZCBjb2x1bW5zIHJlbmFtZWQgYXMvaWYgbmVjZXNzYXJ5Lg0KDQojIyBCaWFzDQoNCkFmdGVyIHRoaXMgZGF0YSBjbGVhbmluZyBhbmQgdHJhbnNmb3JtYXRpb24gcHJvY2VzcywgSSBhc3N1bWUgdGhhdCBhbnkgcG9zc2libGUgYmlhcyBpbiB0aGUgZGF0YSBoYXMgYmVlbiBzdWJzdGFudGlhbGx5IHJlZHVjZWQgdG8gYSBtaW5pbXVtLiANCg0KIyBJbnN1ZmZpY2llbnQgZGF0YQ0KDQpCZWNhdXNlIHRoZXJlIGlzIG5vIHRpbWUgdG8gY29sbGVjdCBuZXcgZGF0YSwgYW5kIGV4dHJhIGRhdGEgaXMgbm90IGF2YWlsYWJsZSwgdGhlIGFuYWx5c2lzIHdpbGwgYmUgYmFzZWQgb24gdGhlIGV4aXN0aW5nIGRhdGEgb25seS4gDQoNCiMgRGF0YSBhbmFseXNpcw0KDQojIyBTdW1tYXJ5IHN0YXRpc3RpY3MgYWJvdXQgZWFjaCBkYXRhZnJhbWUNCg0KIyMjIEZvciB0aGUgZGFpbHkgYWN0aXZpdHkgZGF0YWZyYW1lOg0KDQpgYGB7cn0NCmRhaWx5YWN0aXZpdHkgJT4lICANCiAgc2VsZWN0KFRvdGFsU3RlcHMsICBTZWRlbnRhcnlNaW51dGVzLCBGYWlybHlBY3RpdmVNaW51dGVzLCBWZXJ5QWN0aXZlTWludXRlcywNCiAgICAgICAgIENhbG9yaWVzKSAlPiUNCiAgc3VtbWFyeSgpDQpgYGANCiMjIyBGb3IgdGhlIHNsZWVwZGF5IHVuaXF1ZSBkYXRhZnJhbWU6DQoNCmBgYHtyfQ0Kc2xlZXBkYXl1bmlxdWUgJT4lICANCiAgc2VsZWN0KFRvdGFsU2xlZXBSZWNvcmRzLA0KICAgICAgICAgVG90YWxNaW51dGVzQXNsZWVwLA0KICAgICAgICAgVG90YWxUaW1lSW5CZWQpICU+JQ0KICBzdW1tYXJ5KCkNCmBgYA0KIyMjIEV4cGxvcmluZyBzb21lIHBsb3RzDQoNCkxldCBzZWUgd2hhdCB0aGVzZSBzdW1tYXJpZXMgdGVsbCB1cyBhYm91dCBob3cgdGhpcyBzYW1wbGUgb2YgcGVvcGxl4oCZcyBhY3Rpdml0aWVzLCBzdGFydGluZyB3aXRoIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0b3RhbCBzdGVwcyB0YWtlbiBhbmQgY2Fsb3JpZXMgdXNlZC4NCg0KYGBge3J9DQpnZ3Bsb3QoZGFpbHlhY3Rpdml0eSkgKyANCiAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4PVRvdGFsU3RlcHMsIHk9Q2Fsb3JpZXMpKSArDQogIGxhYnModGl0bGUgPSAnVG90YWwgc3RlcHMgdGFrZW4gdnMgY2Fsb3JpZXMgYnVybmVkJykNCmBgYA0KDQpUaGlzIHBsb3Qgc2hvd3MgdGhhdCB0aGUgbW9yZSBzdGVwcyBhcmUgdGFrZW4sIHRoZSBtb3JlIGNhbG9yaWVzIGFyZSB1c2VkL2xvc3QvYnVybmVkLCBhbHRob3VnaCBpdCBzZWVtcyB0byBzbG93IGRvd24gd2hlbiBpdCBzdGFydCBnZXR0aW5nIHRvIHZlcnkgaGlnaCBudW1iZXIgb2Ygc3RlcHMgdGFrZW4uIFRoaXMgd2lsbCBoZWxwIGRpcmVjdCB0aGUgbWFya2V0aW5nIHRvIGN1c3RvbWVycyB3aG8gd291bGQgYmUgbG9va2luZyB0byBidXJuIG1vcmUgY2Fsb3JpZXMgZm9yIGFueSByZWFzb24gc3VjaCBhcyB3YW50aW5nIHRvIGxvc2Ugd2VpZ2h0LCBvciB0aG9zZSB3aG8gbG9zdCBhcHBldGl0ZSBhbmQgd2FudCB0byByZXRyaWV2ZSBpdCBhZ2Fpbiwgc28gdGhhdCB0aGV5IGNhbiBzdGFydCB3YWxraW5nIG1vcmUuIFRoZSBtYXJrZXRpbmcgdGVhbSBjb3VsZCBhbHNvIHVzZSB0aGlzIGluZm8gdG8gYWR2ZXJ0aXNlIHRvIGF0aGxldGVzIHdobyB3b3VsZCB3YW50IHRvIG1lYXN1cmUgdGhlIHN0ZXBzIHRoZXkgdGFrZSBhIGRheSBhZ2FpbnN0IHRoZSBjYWxvcmllcyB0aGV5IHdvdWxkIG5lZWQuDQoNCkxldCBzZWUgaG93IGlzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0b3RhbCBtaW51dGVzIGFzbGVlcCBhbmQgdG90YWwgdGltZSBpbiBiZWQuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBzbGVlcGRheXVuaXF1ZSkgKyBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHg9VG90YWxNaW51dGVzQXNsZWVwLCB5PVRvdGFsVGltZUluQmVkICkpICsgbGFicyh0aXRsZSA9ICdUb3RhbCBtaW51dGVzIGFzbGVlcCB2cyB0b3RhbCBtaW51dGVzIGluIGJlZCcpDQpgYGANCg0KDQpUaGlzIGdyYXBoIGlzIG5vdCBsaW5lYXIgYXMgb25lIHdvdWxkIGV4cGVjdCwgc2hvd2luZyB0aGF0IHRoZSB0aW1lIGluIGJlZCBpcyBub3QgZXF1YWwgdG8gdGhlIHRpbWUgYXNsZWVwLiBUaGlzIG1lYW5zIHRoYXQgZnVydGhlciBleHBsb3JhdGlvbiBpcyBuZWVkZWQgYmVmb3JlIG1ha2luZyBhIGNvbmNsdXNpb24gaGVyZS4NCg0KTGV04oCZcyBub3cgbWVyZ2UgdGhlc2UgdHdvIGRhdGEgc2V0cy4NCg0KYGBge3J9DQpjb21iaW5lZGRhdGEgPC0gbWVyZ2UoZGFpbHlhY3Rpdml0eSwgc2xlZXBkYXl1bmlxdWUsIGJ5PSJJZCIpDQpoZWFkKGNvbWJpbmVkZGF0YSkNCmBgYA0KDQoNCk5vdywgbGV04oCZcyBsb29rIGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBzdGVwcyB0YWtlbiBhbmQgc2xlZXAgdGltZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGNvbWJpbmVkZGF0YSwgbWFwcGluZyA9IGFlcyh4PVRvdGFsU3RlcHMsIHk9VG90YWxNaW51dGVzQXNsZWVwKSkrIGdlb21fc21vb3RoKCkgKyBsYWJzKHRpdGxlID0gJ1RvdGFsIHN0ZXBzIHRha2VuIHZzIHRvdGFsIG1pbnV0ZXMgYXNsZWVwJykNCmBgYA0KDQpUaGVyZSBpcyBubyBjbGVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVzZSB0d28gdmFyaWFibGUsIGZvciBpbnN0YW5jZSBpbmRpY2F0aW5nIHRoYXQgdGhlIG1vcmUgc3RlcHMsIHRoZSBtb3JlIG9yIGxlc3Mgc2xlZXAgdGltZSwgYXMgaXQgc2VlbXMgdG8gdmFyeSB3aXRoIHRoZSBsaW5lIGdyYXBoIHN0YXJ0aW5nIGhpZ2gsIHRoZW4gZ29pbmcgZG93biBhbmQgdXAgYWdhaW4uIFRoaXMgaW5mbyBtYXkgaGVscCB0aGUgbWFya2V0aW5nIHRlYW0gdG8gZHJhdyBhdHRlbnRpb24gb2YgdGhlIGN1c3RvbWVycyB0byB0aGUgZmFjdCB0aGF0IHRha2luZyBtb3JlIHN0ZXBzIHdpbGwgbm90IG5lY2Vzc2FyeSBtYWtlIHRoZW0gc2xlZXAgbW9yZS4gQW4gYWR2aWNlIG9uIHRoZSBhdmVyYWdlIHN0ZXBzIG1heSBiZSByZWNvbW1lbmRlZC4NCg0KTGV04oCZcyBhbHNvIGxvb2sgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIFZlcnkgYWN0aXZlIG1pbnV0ZXMgYW5kIG1pbnV0ZXMgYXNsZWVwLg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gY29tYmluZWRkYXRhLCBtYXBwaW5nID0gYWVzKHg9VmVyeUFjdGl2ZU1pbnV0ZXMsIHk9VG90YWxNaW51dGVzQXNsZWVwKSkgKyBnZW9tX3Ntb290aCgpICsNCiAgbGFicyh0aXRsZSA9ICdWZXJ5IGFjdGl2ZSBtaW51dGVzIHZzIHRvdGFsIG1pbnV0ZXMgYXNsZWVwJykNCmBgYA0KDQpIZXJlIHRvbywgdGhlcmUgaXMgbm8gY2xlYXIgY29uY2x1c2lvbiB0byBiZSBkcmF3biBzaW5jZSB0aGUgcmVsYXRpb25zaGlwIGZsdWN0dWF0ZXMgYSBsb3QsIHNob3dpbmcgYSBoaWdoIGFzbGVlcCB0aW1lIGF0IHplcm8gbWludXRlcyBhY3Rpdml0eSwgdGhlbiB2ZXJ5IGxvdyBzbGVlcCB0aW1lIGF0IGFyb3VuZCA0MCBtaW51dGVzIGFjdGl2aXR5LCBhbmQgZ29pbmcgaGlnaCBhZ2FpbiBhdCBhcm91bmQgYW4gaG91ciBhY3Rpdml0eSwgYW5kIGRvd24gYWdhaW4uDQoNCkxldOKAmXMgbm93IGxvb2sgYXQgd2hhdCBpcyBoYXBwZW5pbmcgYmV0d2VlbiBzZWRlbnRhcnkgbWludXRlcyBhbmQgdG90YWwgbWludXRlcyBhc2xlZXAuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBjb21iaW5lZGRhdGEsIG1hcHBpbmcgPSBhZXMoeD1TZWRlbnRhcnlNaW51dGVzLCB5PVRvdGFsTWludXRlc0FzbGVlcCkpICsgZ2VvbV9zbW9vdGgoKSArDQogIGxhYnModGl0bGUgPSAnU2VkZW50YXJ5IG1pbnV0ZXMgdnMgdG90YWwgbWludXRlcyBhc2xlZXAnKQ0KYGBgDQoNCkhlcmUgYWdhaW4sIHRoZXJlIGlzIG5vIGNsZWFyIGNvbmNsdXNpb24gdG8gYmUgZHJhd24gc2luY2UgdGhlIHJlbGF0aW9uc2hpcCBmbHVjdHVhdGVzIGEgbG90LCBzaG93aW5nIGEgcmVsYXRpdmVseSBoaWdoIGFzbGVlcCB0aW1lIGF0IHplcm8gc2VkZW50YXJ5IG1pbnV0ZXMgdG8gYSBoaWdoZXN0IGFzbGVlcCB0aW1lIGFyb3VuZCA0MDAgc2VkZW50YXJ5IG1pbnV0ZXMsIHRoZW4gZG93biB2ZXJ5IGxvdyBzbGVlcCB0aW1lIGF0IGFyb3VuZCAxMTUwIHNlZGVudGFyeSBtaW51dGVzLCBhbmQgZ29pbmcgdXAgYWdhaW4uDQoNCkZpbmFsbHksIGxldCBzZWUgd2hhdCBpcyBoYXBwZW5pbmcgaW4gYmV0d2VlbiB2ZXJ5IGFjdGl2ZSBtaW51dGVzIGFuZCBzZWRlbnRhcnkgbWludXRlcyBieSBsb29raW5nIGF0IGZhaXJseSBhY3RpdmUgbWludXRlcyBhZ2FpbnN0IHRvdGFsIG1pbnV0ZXMgYXNsZWVwLg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gY29tYmluZWRkYXRhLCBtYXBwaW5nID0gYWVzKHg9RmFpcmx5QWN0aXZlTWludXRlcywgeT1Ub3RhbE1pbnV0ZXNBc2xlZXApKSArIGdlb21fc21vb3RoKCkgKw0KICBsYWJzKHRpdGxlID0gJ0ZhaXJseSBhY3RpdmUgbWludXRlcyB2cyB0b3RhbCBtaW51dGVzIGFzbGVlcCcpDQpgYGANCg0KVGhpcyBncmFwaCBzaG93cyBhIGJldHRlciByZWxhdGlvbnNoaXAsIGV2ZW4gdGhvdWdoIG5vdCBwZXJmZWN0LCB0aGF0IGxvdyB0byBmYWlybHkgYWN0aXZlIG1pbnV0ZXMgcmVzdWx0IGluIGxvbmdlciBzbGVlcCB0aW1lLiBJdCBjYW4gYmUgbm90aWNlZCB0aGF0IHRoZSBzbGVlcCB0aW1lIGdyYWR1YWxseSBkZWNyZWFzZXMgYXMgYWN0aXZlIG1pbnV0ZXMgaW5jcmVhc2UgYmVmb3JlIHN0YXJ0aW5nIHRvIHBpY2sgdXAgc2xpZ2h0bHkgYWdhaW4gYWZ0ZXIgMTAwIG9mIGFjdGl2ZSBtaW51dGVzLiBGcm9tIHRoZXNlIGxhc3QgdGhyZWUgZ3JhcGhzIGFuYWx5c2lzLCB0aGUgbWFya2V0aW5nIHRlYW0gaXMgYWJsZSB0byBhZHZpc2UgY3VzdG9tZXJzIG9uIGhvdyBhY3RpdmUgaXMgZW5vdWdoIGZvciBhIGdvb2Qgc2xlZXAsIGFuZCByZWNvbW1lbmQgYW4gYXZlcmFnZSBudW1iZXIgb2YgbWludXRlcyBvZiBhY3Rpdml0aWVzLg0KDQpJbiBjb25jbHVzaW9uLCBhbmFseXNpbmcgdGhlc2UgYWN0aXZpdGllcyBhbmQgdmlzdWFsaXppbmcgdGhlbSBnaXZlcyBhIGdvb2QgaWRlYSB0byB0aGUgQmVsbGFiZWF0IG1hcmtldGluZ24gdGVhbSBvbiB0aGUgc3RyYXRlZ2llcyB0byBhZG9wdCBmb3IgdGhlaXIgYWR2ZXJ0c2luZyBjYW1wYWlnbi4gQXNraW5nIG1vcmUgb2YgdGhlaXIgY2xpZW50cyB0byByZWd1bGFybHkgcmVwb3J0IGRhdGEgb24gYW4gb25saW5lIHBvcnRhbCBvciByZXF1ZXN0aW5nIHRvIGNvbm5lY3QgdGhlIHZhcmlvdXMgYXBwcyBhbmQgdG8gdGhlIGNvbXBhbnkncyBzZXJ2ZXIgdG8gYWxsb3cgdGhlIGNyZWF0aW9uIG9mIGRhc2hib2FyZHMgdG8gbW9uaXRvciB0aGUgdG9vbHMgdXNlIHNob3VsZCBiZSBjb25zaWRlcmVkIGZvciBiZXR0ZXIgZGVjaXNpb24gbWFraW5nLiA=