Overview

Introduction

Subsetting data is an essential part of the transform stage in the data science workflow. It involves extracting a portion of the dataset based on specific conditions.

In this short tutorial, we will learn:

  1. What subsetting means and why it is crucial
  2. How to perform subsetting using conditional operators and the dplyr functions filter() and select()

Why is subsetting a dataset important?

Most real-world datasets are large and complex, with many variables and thousands of records.

Most real-world datasets are large and complex, with many variables and thousands of records. Analyzing all of it at once can introduce noise, slow down computation, and make it harder to detect meaningful patterns. By filtering rows and selecting specific columns, we reduce this complexity, improve clarity, and can more efficiently test hypotheses or build models. Subsetting also plays a key role in ensuring data quality, allowing us to remove irrelevant or problematic entries like missing values, outliers, or categories outside the scope of our analysis.

Filtering rows and selecting specific columns helps us:

  • Reduce dataset complexity and improve clarity, making it easier to test hypotheses efficiently
  • Remove irrelevant or problematic entries such as missing values, outliers, or unused categories, improving overall data quality

The original dataset is very large—it includes 848 variables and 17,000+ observations.

The dataset we have used in previous sessions originated from the [General Social Survey, Cycle 29 (2015)] (https://odesi.ca/en/details?id=/odesi/doi__10-5683_SP3_RDS0CK.xml), from the Social and Aboriginal Statistics Division at Statistics Canada. This survey tracks how Canadians spend and manage their time, helping us understand patterns tied to well-being and stress. However, the original dataset is very large—it includes over 848 variables and more than 17,000 observations.

For the purposes of this tutorial, we are not interested in every variable. Instead, we are working with a subset of this dataset: 29 variables focused mainly on time durations and key demographic characteristics. This makes the data more manageable and relevant for our exploration of time use and perceptions of time pressure.

Framing the Guiding Question: Who Feels Rushed?

Let’s return to the time usage dataset. In this section, suppose we’re interested in understanding how people who feel rushed spend their time differently from those who don’t. To answer this question, we don’t need every single row or column, as working with the data in its original format would be unnecessarily complex. Instead, we need to “get inside” our data by subsetting: filtering the rows and selecting the columns that matter.

We will explore our dataset through one guiding question:

How do people who feel rushed spend their time differently from those who don’t?

We’ll focus on the relevant rows and columns that answer this question.

Step by Step

0: Setup and Load the Data

Load and Understand the Dataset

First, we need to load the necessary libraries for this session.

library(dplyr)

In our subsequent tasks, the dplyr package will be essential for subsetting operations such as filtering rows and selecting columns. We can either continue using the js_data object from the previous section or load the timeuse_day3_1.Rdata file from the data folder.

js_data_path <- "data/timeuse_day3_1.Rdata"
load(js_data_path)

Now, let’s again examine our dataset structure by displaying the first few rows by using head() function.

js_data |>
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact
10000 5 1 5 46 1 3 1 1 510 60 120 770 90 0 0 0 0 NA 1 1 1 2 NA 8 2 2 2 2 MB Divorced Trade certificate or diploma
10001 5 1 1 59 1 4 3 4 420 150 0 0 0 0 0 0 0 NA 2 1 1 2 NA 1 2 2 2 2 BC Married College, CEGEP, or other non-university certificate or dimploma
10002 4 2 1 47 1 5 1 6 570 0 0 630 30 480 0 0 0 NA NA NA 1 1 NA 7 2 1 1 1 SK Married University certificate or dimploma below the bachelor’s level
10003 6 2 5 35 1 4 2 4 510 10 45 875 80 20 0 0 0 NA NA NA 1 1 NA 1 2 2 2 2 ON Divorced College, CEGEP, or other non-university certificate or dimploma
10004 2 1 6 35 1 NA 1 3 525 90 40 815 0 0 0 0 0 NA NA NA 2 2 NA 1 2 2 2 2 ON Single, never married NA
10005 1 1 6 35 1 1 1 6 435 0 0 430 40 530 0 0 0 NA NA NA 1 1 NA 2 2 1 1 2 ON Single, never married Less than high school dimploma or its equivalent

As we can see, there are 30 columns in the dataset, which is a lot to work with. For now, we can focus on the following key columns:

  • id: Record identification
  • ageGrp: Age group of respondent (groups of 10)
  • sex: Sex of respondent
  • maritalStat: Marital status of the respondent
  • province: Province of residence
  • popCenter: Population centre indicator
  • eduLevel: Educational attainment (highest degree)
  • feelRushed: General time use – Feel rushed
  • extraTime: General time use – Extra time
  • durSleep: Duration – Sleeping, resting, relaxing, sick in bed
  • durWork: Duration – Paid work
  • timeWorkaholic: Perceptions of time – Workaholic
  • timeWantAlone: Perceptions of time – Would like more time alone

These columns provide information on demographics, time usage, and time perceptions. We can use them to explore patterns in work-life balance, education, and social time.

Now that we understand our dataset structure, let’s move on to learning how to filter and manipulate this data effectively using Boolean operators in dplyr.

1: Learning About Filtering

Conditional Filtering with Boolean Operators using dplyr

dplyr is a powerful package that lets us extract and transform data with a clear, readable syntax. In dplyr, we use functions like filter(), select(), and mutate() to work with our data. Boolean operators (==, <, >, <=, >=, and !=) are used within these functions to test conditions, and we can combine conditions with & (and) or | (or).

Let’s start by understanding the basic comparison operators that will help us create filtering conditions.

Comparison Operators

Comparison operators allow us to check conditions within our dataset. These return TRUE or FALSE based on whether the condition is met.

Operator Meaning Example Result
== Equal to 5 == 5 TRUE
!= Not equal to 5 != 3 TRUE
< Less than 3 < 5 TRUE
> Greater than 5 > 3 TRUE
<= Less than or equal to 3 <= 3 TRUE
>= Greater than or equal to 5 >= 3 TRUE

Once we understand these basic comparison operators, we can combine them using logical operators to create more complex filtering conditions.

Logical Operators

Logical operators allow us to filter data based on multiple conditions.

Operator Meaning Example Result
& Logical AND (Both conditions must be TRUE) (5 > 3) & (4 < 6) TRUE
| Logical OR (At least one condition must be TRUE) (5 > 3) | (4 > 6) TRUE
! Logical NOT (Reverses TRUE/FALSE) !(5 > 3) FALSE

Let’s try using these logical operators to filter the rows in the following example.

We want to understand whether feeling rushed might relate to how much time is spent on work, sleep, or alone time. Therefore, the columns of interest are feelRushed, durSleep, durWork, and durAlone. First, let’s explore the values in these columns using dplyr.

Let’s look at the unique values in the feelRushed column to understand what categories exist in our data before we start filtering based on these values.

js_data |> 
  distinct(feelRushed)
## # A tibble: 7 × 1
##   feelRushed
##        <dbl>
## 1          1
## 2          3
## 3          2
## 4          4
## 5          5
## 6          6
## 7         NA

Since our duration columns (durSleep, durWork, and durAlone) are continuous numerical variables rather than categorical, it’s more informative to examine their distributions rather than just their unique values. Looking at the distribution helps us understand which values are common, identify patterns, and spot potential outliers:

js_data |> 
  count(durSleep) |> 
  arrange(desc(n)) |> 
  head(10) 
## # A tibble: 10 × 2
##    durSleep     n
##       <dbl> <int>
##  1      480  1323
##  2      540  1223
##  3      510  1093
##  4      450   854
##  5      600   849
##  6      420   810
##  7      570   784
##  8      630   455
##  9      390   443
## 10      660   358
js_data |> 
  count(durWork) |> 
  arrange(desc(n)) |> 
  head(10)
## # A tibble: 10 × 2
##    durWork     n
##      <dbl> <int>
##  1       0 11243
##  2     480   437
##  3     510   237
##  4     450   231
##  5     540   230
##  6     420   213
##  7     600   140
##  8     495   138
##  9     465   125
## 10     525   113
js_data |> 
  count(durAlone) |> 
  arrange(desc(n)) |> 
  head(10) 
## # A tibble: 10 × 2
##    durAlone     n
##       <dbl> <int>
##  1     1440  1459
##  2        0  1122
##  3       60   244
##  4       30   216
##  5      120   198
##  6      180   179
##  7      150   162
##  8       90   159
##  9     1380   156
## 10      240   154

These commands work together to show the most common time patterns:

  • count() tallies each duration value,
  • arrange(desc(n)) sorts from highest to lowest frequency,
  • head(10) keeps only the top 10 results

This quick overview helps us understand typical time allocation patterns for sleep, work, and being alone.

It appears that all the columns contain numeric values. However, as we look at the data dictionary, we’ll see that only durWork, durSleep and durAlone have numeric values that represent real quantities (minutes of work, sleep or time alone in a day). In contrast, the values in the feelRushed column have a different meaning.

Code Value
1 daily
2 few Times a Week
3 once a Week
4 once a Month
5 less a Month
6 never

2: Apply Basic Filtering

Filtering Rows with dplyr

For this analysis, we will consider respondents who report feeling rushed as those whose frequency of feeling this way is at least once a week, and those who feel this way less than once a week as not feeling rushed.

Extracting Respondents Who Do or Do not Feel Rushed Daily

First, let’s extract only the respondents who report feeling rushed daily. Looking at the table, the value corresponding to feeling rushed daily is 1. We can filter for these respondents using filter().

The filter() function in dplyr returns only the rows that meet a specified condition.

We filter any row which has feelRushed equal to 1 by passing the Boolean expression feelRushed == 1 into filter, and then use head() to see what the filtered data look like.

js_data |> 
  filter(feelRushed == 1) |> 
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact
10000 5 1 5 46 1 3 1 1 510 60 120 770 90 0 0 0 0 NA 1 1 1 2 NA 8 2 2 2 2 MB Divorced Trade certificate or diploma
10002 4 2 1 47 1 5 1 6 570 0 0 630 30 480 0 0 0 NA NA NA 1 1 NA 7 2 1 1 1 SK Married University certificate or dimploma below the bachelor’s level
10004 2 1 6 35 1 NA 1 3 525 90 40 815 0 0 0 0 0 NA NA NA 2 2 NA 1 2 2 2 2 ON Single, never married NA
10005 1 1 6 35 1 1 1 6 435 0 0 430 40 530 0 0 0 NA NA NA 1 1 NA 2 2 1 1 2 ON Single, never married Less than high school dimploma or its equivalent
10011 2 2 6 35 1 3 1 6 630 60 20 235 65 475 0 0 0 NA NA NA 1 1 NA 2 1 2 1 2 ON Single, never married Trade certificate or diploma
10012 2 2 1 35 2 7 1 2 390 0 50 90 90 480 0 0 0 NA NA NA 1 1 NA 2 2 2 1 2 ON Married University certificate, diploma, degree above the BA level

Looking at the filtered data, we can confirm that our filter worked correctly—all rows show feelRushed equal to 1 (meaning they feel rushed daily).

Using filter(), we subset the data to keep only those rows where the condition is met. In the above example, the condition feelRushed == 1 creates a logical vector that is TRUE for rows where the value equals 1 (corresponding to “daily”).

Using Comparison and Logical Operators

Not only can we use the == operator to test for equality, but we can also use operators such as <, >, <=, >=, and != to compare values. For example, we might filter rows where a numeric variable exceeds a certain threshold, is below a limit, or is not equal to a specified value. Let’s use an example from the durSleep column.

For instance, if we want to filter rows for the durSleep column to capture any instances with sleep duration less than 600, we can write:

js_data |> 
  filter(durSleep < 600) |>
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact
10000 5 1 5 46 1 3 1 1 510 60 120 770 90 0 0 0 0 NA 1 1 1 2 NA 8 2 2 2 2 MB Divorced Trade certificate or diploma
10001 5 1 1 59 1 4 3 4 420 150 0 0 0 0 0 0 0 NA 2 1 1 2 NA 1 2 2 2 2 BC Married College, CEGEP, or other non-university certificate or dimploma
10002 4 2 1 47 1 5 1 6 570 0 0 630 30 480 0 0 0 NA NA NA 1 1 NA 7 2 1 1 1 SK Married University certificate or dimploma below the bachelor’s level
10003 6 2 5 35 1 4 2 4 510 10 45 875 80 20 0 0 0 NA NA NA 1 1 NA 1 2 2 2 2 ON Divorced College, CEGEP, or other non-university certificate or dimploma
10004 2 1 6 35 1 NA 1 3 525 90 40 815 0 0 0 0 0 NA NA NA 2 2 NA 1 2 2 2 2 ON Single, never married NA
10005 1 1 6 35 1 1 1 6 435 0 0 430 40 530 0 0 0 NA NA NA 1 1 NA 2 2 1 1 2 ON Single, never married Less than high school dimploma or its equivalent

Alternatively, we can filter rows where durSleep is between 600 and 1000. To do this, we chain two conditions using the & operator:

js_data |> 
  filter(durSleep >= 600 & durSleep <= 1000) |>
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact
10006 1 1 6 35 1 1 4 2 635 60 50 650 0 0 0 0 0 NA 2 1 1 2 NA 3 NA 2 2 2 ON Single, never married Less than high school dimploma or its equivalent
10011 2 2 6 35 1 3 1 6 630 60 20 235 65 475 0 0 0 NA NA NA 1 1 NA 2 1 2 1 2 ON Single, never married Trade certificate or diploma
10016 7 1 1 46 2 7 6 6 660 60 0 1440 0 0 0 0 0 NA 2 2 2 2 NA 8 2 2 2 2 MB Married University certificate, diploma, degree above the BA level
10021 5 2 1 12 1 6 6 1 660 0 50 640 0 0 0 0 0 NA 2 1 1 2 NA 8 2 2 2 2 NS Married Bachelor’s degree
10025 2 1 6 24 1 4 4 6 660 240 60 1440 0 0 0 0 0 NA NA NA 1 1 NA 7 2 2 2 2 QC Single, never married College, CEGEP, or other non-university certificate or dimploma
10027 1 1 6 13 1 1 2 1 600 0 30 765 0 0 330 0 0 1 1 2 2 2 1 5 1 2 1 2 NB Single, never married Less than high school dimploma or its equivalent

In this example:

  • The condition durSleep >= 600 checks for rows where sleep duration is at least 600.
  • The condition durSleep <= 1000 checks for rows where sleep duration is at most 1000.
  • The & operator combines these conditions, ensuring that only rows satisfying both conditions are returned.

Here we’ve used the & operator for “and” conditions, but we can also use the | operator to specify “or” conditions. By using these boolean operators, we can chain multiple conditions together.

3: Apply Complex Filtering

Complex Filtering with dplyr: Filtering Rows for Those Who Feel Rushed

Now, let’s perform a more complex filtering. Suppose we want to capture respondents who feel rushed frequently—that is, those whose feelRushed value is either 1, 2, or 3—and those who do not feel rushed frequently, meaning those whose feelRushed value is either 4, 5, or 6.

There are multiple ways to filter rows that meet one of these conditions.

Using Range Comparison

The first method uses a range comparison with <= to filter the rows.

js_data |> 
  filter(feelRushed <= 3) |>
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact
10000 5 1 5 46 1 3 1 1 510 60 120 770 90 0 0 0 0 NA 1 1 1 2 NA 8 2 2 2 2 MB Divorced Trade certificate or diploma
10001 5 1 1 59 1 4 3 4 420 150 0 0 0 0 0 0 0 NA 2 1 1 2 NA 1 2 2 2 2 BC Married College, CEGEP, or other non-university certificate or dimploma
10002 4 2 1 47 1 5 1 6 570 0 0 630 30 480 0 0 0 NA NA NA 1 1 NA 7 2 1 1 1 SK Married University certificate or dimploma below the bachelor’s level
10003 6 2 5 35 1 4 2 4 510 10 45 875 80 20 0 0 0 NA NA NA 1 1 NA 1 2 2 2 2 ON Divorced College, CEGEP, or other non-university certificate or dimploma
10004 2 1 6 35 1 NA 1 3 525 90 40 815 0 0 0 0 0 NA NA NA 2 2 NA 1 2 2 2 2 ON Single, never married NA
10005 1 1 6 35 1 1 1 6 435 0 0 430 40 530 0 0 0 NA NA NA 1 1 NA 2 2 1 1 2 ON Single, never married Less than high school dimploma or its equivalent
js_data |> 
  filter(feelRushed > 3 & feelRushed <= 6) |>
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact
10006 1 1 6 35 1 1 4 2 635 60 50 650 0 0 0 0 0 NA 2 1 1 2 NA 3 NA 2 2 2 ON Single, never married Less than high school dimploma or its equivalent
10007 5 2 3 59 1 4 5 3 440 30 160 1200 60 0 0 0 0 NA 2 2 2 2 NA 8 2 2 1 2 BC Widowed College, CEGEP, or other non-university certificate or dimploma
10009 6 1 3 46 1 3 6 2 540 20 120 1060 70 0 0 0 0 NA 2 2 2 2 NA 8 2 2 2 2 MB Widowed Trade certificate or diploma
10013 3 1 1 24 1 6 4 6 510 0 20 200 90 0 0 0 0 2 1 2 2 2 2 1 2 1 2 2 QC Married Bachelor’s degree
10016 7 1 1 46 2 7 6 6 660 60 0 1440 0 0 0 0 0 NA 2 2 2 2 NA 8 2 2 2 2 MB Married University certificate, diploma, degree above the BA level
10021 5 2 1 12 1 6 6 1 660 0 50 640 0 0 0 0 0 NA 2 1 1 2 NA 8 2 2 2 2 NS Married Bachelor’s degree

Now let’s calculate how many rows remain after we performed filtering.

all_rows <- js_data |> 
  nrow()
rushed_rows <- js_data |> 
  filter(feelRushed <= 3) |>
  nrow()
not_rushed_rows <- js_data |> 
  filter(feelRushed > 3 & feelRushed <= 6) |>
  nrow()

Let’s print and see the number of rows in each dataframe after filtering.

print(paste("The number of rows in data is:", all_rows))
## [1] "The number of rows in data is: 17390"
print(paste("The number of rows in rushed is:", rushed_rows))
## [1] "The number of rows in rushed is: 12689"
print(paste("The number of rows in not rushed is:", not_rushed_rows))
## [1] "The number of rows in not rushed is: 4639"
  • This approach selects rows where feelRushed is less than or equal to 3 (i.e., values 1, 2, or 3) to indicate respondents who feel rushed.
  • Rows where feelRushed is greater than or equal to 4 and less than or equal to 6 are considered not rushed.
  • We use the & operator to chain the two Boolean operations.
  • The nrow() function calculates the number of rows in the data frame.

As we can see, the original data frame has 17,390 rows; after filtering, the rushed data frame contains only 12,689 rows.

Chaining Multiple Conditions

Alternatively, we can use a second method that involves chaining three conditions using the | operator. While this approach is more verbose, it makes the logic very explicit:

js_data |> 
  filter(feelRushed == 1 | feelRushed == 2 | feelRushed == 3) |> 
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact
10000 5 1 5 46 1 3 1 1 510 60 120 770 90 0 0 0 0 NA 1 1 1 2 NA 8 2 2 2 2 MB Divorced Trade certificate or diploma
10001 5 1 1 59 1 4 3 4 420 150 0 0 0 0 0 0 0 NA 2 1 1 2 NA 1 2 2 2 2 BC Married College, CEGEP, or other non-university certificate or dimploma
10002 4 2 1 47 1 5 1 6 570 0 0 630 30 480 0 0 0 NA NA NA 1 1 NA 7 2 1 1 1 SK Married University certificate or dimploma below the bachelor’s level
10003 6 2 5 35 1 4 2 4 510 10 45 875 80 20 0 0 0 NA NA NA 1 1 NA 1 2 2 2 2 ON Divorced College, CEGEP, or other non-university certificate or dimploma
10004 2 1 6 35 1 NA 1 3 525 90 40 815 0 0 0 0 0 NA NA NA 2 2 NA 1 2 2 2 2 ON Single, never married NA
10005 1 1 6 35 1 1 1 6 435 0 0 430 40 530 0 0 0 NA NA NA 1 1 NA 2 2 1 1 2 ON Single, never married Less than high school dimploma or its equivalent
js_data |> 
  filter(feelRushed == 4 | feelRushed == 5 | feelRushed == 6) |>
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact
10006 1 1 6 35 1 1 4 2 635 60 50 650 0 0 0 0 0 NA 2 1 1 2 NA 3 NA 2 2 2 ON Single, never married Less than high school dimploma or its equivalent
10007 5 2 3 59 1 4 5 3 440 30 160 1200 60 0 0 0 0 NA 2 2 2 2 NA 8 2 2 1 2 BC Widowed College, CEGEP, or other non-university certificate or dimploma
10009 6 1 3 46 1 3 6 2 540 20 120 1060 70 0 0 0 0 NA 2 2 2 2 NA 8 2 2 2 2 MB Widowed Trade certificate or diploma
10013 3 1 1 24 1 6 4 6 510 0 20 200 90 0 0 0 0 2 1 2 2 2 2 1 2 1 2 2 QC Married Bachelor’s degree
10016 7 1 1 46 2 7 6 6 660 60 0 1440 0 0 0 0 0 NA 2 2 2 2 NA 8 2 2 2 2 MB Married University certificate, diploma, degree above the BA level
10021 5 2 1 12 1 6 6 1 660 0 50 640 0 0 0 0 0 NA 2 1 1 2 NA 8 2 2 2 2 NS Married Bachelor’s degree

This method very explicitly tests for rows where the feelRushed value is either 1, 2, or 3, or for rows where the value is either 4, 5 or 6.

Again, let’s calculate how many rows remain after we performed filtering.

rushed_rows <- js_data |> 
  filter(feelRushed == 1 | feelRushed == 2 | feelRushed == 3) |>
  nrow()
not_rushed_rows <- js_data |> 
  filter(feelRushed == 4 | feelRushed == 5 | feelRushed == 6) |>
  nrow()

print(paste("The number of rows in rushed is:", rushed_rows))
## [1] "The number of rows in rushed is: 12689"
print(paste("The number of rows in not rushed is:", not_rushed_rows))
## [1] "The number of rows in not rushed is: 4639"

As we can see, looking at the remain rows after filtering, it produces the same output as previously.

There is also a third, more elegant approach using the %in% operator, but we will explore this method in the exercises at the end of this session.

4: Select Relevant Variables

5: Compare Time Use

Comparing Time Usage Between Groups

Now that we have our cleaned datasets for both rushed and not rushed groups, we can analyze how these groups differ in their time allocation patterns. We’ll calculate and compare the mean durations for sleep, work, and alone time between the two groups.

Let’s break this down into steps: 1. First, we’ll calculate the mean values for each time-use variable within each group. 2. Then, we’ll compute the differences between these means to understand the magnitude of variation.

We can create a summary table that contains the mean values of durSleep, durAlone, and durWork for each of the rushed_time and not_rushed_time dataframes:

mean_rushed <- rushed_time |> 
  summarise(
    durSleep = mean(durSleep, na.rm = TRUE),
    durAlone = mean(durAlone, na.rm = TRUE),
    durWork = mean(durWork, na.rm = TRUE)
  )

mean_not_rushed <- not_rushed_time |> 
  summarise(
    durSleep = mean(durSleep, na.rm = TRUE),
    durAlone = mean(durAlone, na.rm = TRUE),
    durWork = mean(durWork, na.rm = TRUE)
  )

How it works:

  • summarise() collapses each data frame down to a single row.
  • Inside, we explicitly name each new column (durSleep, durAlone, durWork) and assign it mean(old_column, na.rm = TRUE).
  • na.rm = TRUE makes sure missing values don’t throw off our averages.

However, we can see there’s a little bit of repetition in the code above. We can write much cleaner code to calculate the mean for every column at once.

# Calculate the mean values for each variable using summarise and across
mean_rushed <- rushed_time |> 
  summarise(across(everything(), ~ mean(. , na.rm = TRUE)))

mean_not_rushed <- not_rushed_time |> 
  summarise(across(everything(), ~ mean(. , na.rm = TRUE)))

In this code:

  • We use summarise() with across() to calculate means for all columns at once
  • The na.rm = TRUE argument ensures we exclude missing values from our calculations

We calculate the difference in means between these two groups.

diff_means <- mean_rushed - mean_not_rushed

The subtraction (mean_rushed - mean_not_rushed) gives a new one‐row tibble showing how much more (or less) time “rushed” individuals spend on each activity compared to “not rushed” individuals.

Now, let’s see what we get up to this point.

## [1] "Mean values for respondents who feel rushed:"
durWork durSleep durAlone
203.5197 515.0236 585.7458
## [1] "Mean values for respondents who do not feel rushed:"
durWork durSleep durAlone
71.17137 542.4402 773.8862
## [1] "Difference between rushed and not rushed (rushed - not rushed):"
durWork durSleep durAlone
132.3483 -27.41654 -188.1403

Positive values in the difference calculation indicate that rushed individuals spend more time on that activity, while negative values indicate they spend less time compared to those who don’t feel rushed.

These results provide interesting insights into how feeling rushed relates to time allocation patterns. For instance, we can observe whether people who feel rushed actually spend more time working or less time sleeping than those who don’t feel rushed, which might help explain their perception of time pressure.

Recode & Save

In the next exercise, we will compare the time pressure felt by respondents based on whether they live in an urban or rural area, as coded in the popCenter column:

js_data |>
  count(popCenter)
## # A tibble: 3 × 2
##   popCenter     n
##       <dbl> <int>
## 1         1 13319
## 2         2  3551
## 3         3   520

According to the data dictionary, these numeric codes mean:

value label
1 Larger urban population centres (CMA/CA)
2 Rural areas and small population centres (non CMA/CA)
3 Prince Edward Island

We can use dplyr’s mutate() along with if_else() to turn those numbers into descriptive labels:

js_data <- js_data |> mutate(
  popCenter=if_else(popCenter==1,'urban',
                    if_else(popCenter==2,'rural','PEI'))
)

How it works:

  • mutate(): adds or modifies columns.
  • if_else(condition, true, false): a vectorized, type-safe conditional.
  • Here, we nest two if_else() calls so that:
    • popCenter == 1 → “urban”
    • popCenter == 2 → “rural”
    • otherwise (code 3) → “PEI”

Next, we’ll create a new flag called isFeelRushed to mark whether each respondent feels rushed or not. This will be useful for our upcoming data-visualization tutorial:

js_data <- js_data |> mutate(
  isFeelRushed=if_else(feelRushed <= 3,1,0)
)

In this code:

  • We assign the result back into js_data so the new column is saved.
  • The if_else() function creates a new binary variable:
    • isFeelRushed = 1 if feelRushed is 1, 2, or 3 (i.e. the respondent feels rushed)
    • isFeelRushed = 0 otherwise (i.e. not rushed)

Finally, save the recoded dataset so it’s ready for the next steps:

save(js_data, file = "data/time_use_day3_2.RData")

Exercises: Time for Practice!

Using %in% Operator

In the previous sections, we learned that we can filter respondents who feel rushed or not by either using a range comparison (e.g., feelRushed <= 3) or by chaining multiple conditions with logical operators (e.g., feelRushed == 1 | feelRushed == 2 | feelRushed == 3).

However, there’s also a third, more elegant approach: using the %in% operator. This method is especially helpful when we want to filter a dataset based on multiple specific values of a variable, making our code shorter and easier to read.

Example Scenario

Remember the previous question where we’re analyzing survey data on how frequently people feel rushed. The feelRushed variable includes values from 1 (daily) to 6 (never). We want to group respondents into two categories:

  • Rushed: those who feel rushed at least once a week (1, 2, 3)
  • Not Rushed: those who feel rushed less often (4, 5, 6)

Task Instructions

  1. Define the sets of values that represent each group.
  2. Use the %in% operator inside filter() to create subsets.
  3. Display the first few rows of each subset to verify correctness.
  4. Count the number of rows in each group to compare sizes.

Steps 1 & 2: Define the sets and use %in% inside filter()

# Define group levels
rushed_levels <- c(1, 2, 3)
not_rushed_levels <- c(4, 5, 6)

# Filter using %in%
js_data |> 
  filter(feelRushed %in% rushed_levels)

js_data |> 
  filter(feelRushed %in% not_rushed_levels)

Step 3a: View the first few rows of rushed_levels

js_data |> 
  filter(feelRushed %in% rushed_levels) |> 
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact isFeelRushed
10000 5 1 5 46 urban 3 1 1 510 60 120 770 90 0 0 0 0 NA 1 1 1 2 NA 8 2 2 2 2 MB Divorced Trade certificate or diploma 1
10001 5 1 1 59 urban 4 3 4 420 150 0 0 0 0 0 0 0 NA 2 1 1 2 NA 1 2 2 2 2 BC Married College, CEGEP, or other non-university certificate or dimploma 1
10002 4 2 1 47 urban 5 1 6 570 0 0 630 30 480 0 0 0 NA NA NA 1 1 NA 7 2 1 1 1 SK Married University certificate or dimploma below the bachelor’s level 1
10003 6 2 5 35 urban 4 2 4 510 10 45 875 80 20 0 0 0 NA NA NA 1 1 NA 1 2 2 2 2 ON Divorced College, CEGEP, or other non-university certificate or dimploma 1
10004 2 1 6 35 urban NA 1 3 525 90 40 815 0 0 0 0 0 NA NA NA 2 2 NA 1 2 2 2 2 ON Single, never married NA 1
10005 1 1 6 35 urban 1 1 6 435 0 0 430 40 530 0 0 0 NA NA NA 1 1 NA 2 2 1 1 2 ON Single, never married Less than high school dimploma or its equivalent 1

Step 3b: View the first few rows of not_rushed_levels

js_data |> 
  filter(feelRushed %in% not_rushed_levels) |> 
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact isFeelRushed
10006 1 1 6 35 urban 1 4 2 635 60 50 650 0 0 0 0 0 NA 2 1 1 2 NA 3 NA 2 2 2 ON Single, never married Less than high school dimploma or its equivalent 0
10007 5 2 3 59 urban 4 5 3 440 30 160 1200 60 0 0 0 0 NA 2 2 2 2 NA 8 2 2 1 2 BC Widowed College, CEGEP, or other non-university certificate or dimploma 0
10009 6 1 3 46 urban 3 6 2 540 20 120 1060 70 0 0 0 0 NA 2 2 2 2 NA 8 2 2 2 2 MB Widowed Trade certificate or diploma 0
10013 3 1 1 24 urban 6 4 6 510 0 20 200 90 0 0 0 0 2 1 2 2 2 2 1 2 1 2 2 QC Married Bachelor’s degree 0
10016 7 1 1 46 rural 7 6 6 660 60 0 1440 0 0 0 0 0 NA 2 2 2 2 NA 8 2 2 2 2 MB Married University certificate, diploma, degree above the BA level 0
10021 5 2 1 12 urban 6 6 1 660 0 50 640 0 0 0 0 0 NA 2 1 1 2 NA 8 2 2 2 2 NS Married Bachelor’s degree 0

Step 4: Count rows

rushed_rows <- js_data |> 
  filter(feelRushed %in% rushed_levels) |> 
  nrow()

not_rushed_rows <- js_data |> 
  filter(feelRushed %in% not_rushed_levels) |> 
  nrow()

print(paste("The number of rows in rushed is:", rushed_rows))
## [1] "The number of rows in rushed is: 12689"
print(paste("The number of rows in not rushed is:", not_rushed_rows))
## [1] "The number of rows in not rushed is: 4639"

Summary

This example shows how the %in% operator simplifies filtering when working with categorical variables. It avoids the need to write multiple == and | conditions, making our code cleaner and easier to read.

Time Pressure Analysis

Let’s use our new filtering and selecting skills to explore how people in urban and rural areas differ in terms of time pressure, based on their reported extra time.

Task Instructions

  1. Explore the popCenter and extraTime columns to understand the variable types and possible values.
  2. Filter the dataset into two groups: urban and rural.
  3. Select the relevant variables: popCenter, extraTime, durWork, and durSleep.
  4. Calculate and compare the average extraTime between the two groups.

Step 1: Explore the Data

js_data |> 
  distinct(popCenter)
popCenter
urban
rural
PEI
js_data |> 
  count(extraTime) |> 
  arrange(desc(n)) |> 
  head()
extraTime n
6 6954
3 2891
2 2662
4 2067
5 1427
1 1313

Step 2: Filter Urban and Rural Populations

To filter the urban population, we use the filter() function to keep only rows where popCenter is equal to "urban". Then, we use head() to display the first few rows of the resulting dataframe:

js_data |> 
  filter(popCenter == 'urban') |>
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact isFeelRushed
10000 5 1 5 46 urban 3 1 1 510 60 120 770 90 0 0 0 0 NA 1 1 1 2 NA 8 2 2 2 2 MB Divorced Trade certificate or diploma 1
10001 5 1 1 59 urban 4 3 4 420 150 0 0 0 0 0 0 0 NA 2 1 1 2 NA 1 2 2 2 2 BC Married College, CEGEP, or other non-university certificate or dimploma 1
10002 4 2 1 47 urban 5 1 6 570 0 0 630 30 480 0 0 0 NA NA NA 1 1 NA 7 2 1 1 1 SK Married University certificate or dimploma below the bachelor’s level 1
10003 6 2 5 35 urban 4 2 4 510 10 45 875 80 20 0 0 0 NA NA NA 1 1 NA 1 2 2 2 2 ON Divorced College, CEGEP, or other non-university certificate or dimploma 1
10004 2 1 6 35 urban NA 1 3 525 90 40 815 0 0 0 0 0 NA NA NA 2 2 NA 1 2 2 2 2 ON Single, never married NA 1
10005 1 1 6 35 urban 1 1 6 435 0 0 430 40 530 0 0 0 NA NA NA 1 1 NA 2 2 1 1 2 ON Single, never married Less than high school dimploma or its equivalent 1

Similarly, we filter the rural population, then preview the first few rows of the resulting dataframe:

js_data |> 
  filter(popCenter == 'rural') |>
  head()
id ageGrp sex maritalStat province popCenter eduLevel feelRushed extraTime durSleep durMealPrep durEating durAlone durDriving durWork durShoolSite durSchoolOnline durStudy mainStudy mainJobHunting mainWork worked12m workedWeek enrollStat dailyTexts timeSlowDown timeWorkaholic timeNotFamFriends timeWantAlone province_fact maritalStat_fact eduLevel_fact isFeelRushed
10008 2 2 1 24 rural 6 2 3 525 40 70 30 5 410 0 0 0 NA NA NA 1 1 NA 1 2 2 2 2 QC Married Bachelor’s degree 1
10012 2 2 1 35 rural 7 1 2 390 0 50 90 90 480 0 0 0 NA NA NA 1 1 NA 2 2 2 1 2 ON Married University certificate, diploma, degree above the BA level 1
10016 7 1 1 46 rural 7 6 6 660 60 0 1440 0 0 0 0 0 NA 2 2 2 2 NA 8 2 2 2 2 MB Married University certificate, diploma, degree above the BA level 0
10018 6 2 1 12 rural 1 3 6 510 15 35 740 330 0 0 0 0 NA NA NA 1 1 NA 1 1 1 1 2 NS Married Less than high school dimploma or its equivalent 1
10030 6 2 1 10 rural 6 1 6 600 135 60 1005 15 0 0 0 0 NA 2 2 2 2 NA 8 2 1 1 1 NL Married Bachelor’s degree 1
10036 3 1 2 24 rural 6 1 6 435 0 45 75 30 720 0 0 0 NA NA NA 1 1 NA 1 2 2 1 2 QC Living common-law Bachelor’s degree 1

Step 3: Select Relevant Variables

We select the following variables:

  • popCenter: to indicate group identity (urban vs. rural)
  • extraTime: to measure perceived time availability
  • durWork: to examine work duration
  • durSleep: to evaluate sleep patterns

We start by selecting the relevant columns from the filtered urban dataframe:

urban_selected <- js_data |> 
  filter(popCenter == 'urban') |> 
  select(popCenter, extraTime, durWork, durSleep)

urban_selected |> 
  head()
popCenter extraTime durWork durSleep
urban 1 0 510
urban 4 0 420
urban 6 480 570
urban 4 20 510
urban 3 0 525
urban 6 530 435

Similarly, we select columns from the filtered rural dataframe:

rural_selected <- js_data |> 
  filter(popCenter == 'rural') |> 
  select(popCenter, extraTime, durWork, durSleep) 
popCenter extraTime durWork durSleep
rural 3 410 525
rural 2 480 390
rural 6 0 660
rural 6 0 510
rural 6 0 600
rural 6 720 435

Step 4: Calculate and Compare Mean Extra Time

To calculate the average amount of extra time reported by urban and rural respondents, we use the summarise() function:

mean_urban <- urban_selected |> 
  summarise(avg_extra_time = mean(extraTime, na.rm = TRUE))
mean_rural <- rural_selected |> 
  summarise(avg_extra_time = mean(extraTime, na.rm = TRUE))
## [1] "Mean extra time for Urban respondents:"
avg_extra_time
4.166139
## [1] "Mean extra time for Rural respondents:"
avg_extra_time
4.255018

It seems that, on average, respondents residing in rural areas have more extraTime. Higher values in extraTime indicate greater availability of free time.

Takeaway

In this session, we learned how to filter rows and select specific columns to create a smaller, cleaner dataset that helps us answer our guiding question more efficiently. Using functions from the dplyr package like filter(), select(), and mutate(), we practiced how to:

  • Filter rows based on conditions using comparison and logical operators
  • Select only the variables we care about using select() and helper functions like starts_with()
  • Use summarise() and across() to calculate mean values for entire groups
  • Recode variables like popCenter and create new flags like isFeelRushed with mutate()

These techniques help us zoom in on the parts of the data that matter most and prepare for deeper exploration in the next sessions.

LS0tCnRpdGxlOiAiRGF0YSBGaWx0ZXJpbmcgYW5kIFNlbGVjdGlvbiBUZWNobmlxdWVzIgpwYWdldGl0bGU6ICJEYXRhIEZpbHRlcmluZyBhbmQgU2VsZWN0aW9uIFRlY2huaXF1ZXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBpbmNsdWRlczoKICAgICAgYWZ0ZXJfYm9keTogZm9vdGVyLmh0bWwKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCgojIyBPdmVydmlldwoKIVtdKGltYWdlcy8zLUFDVC0zLU92ZXJ2aWV3LnBuZykKCiMjIEludHJvZHVjdGlvbgoKOjo6aW50cm8KU3Vic2V0dGluZyBkYXRhIGlzIGFuIGVzc2VudGlhbCBwYXJ0IG9mIHRoZSA8ZW0+dHJhbnNmb3JtPC9lbT4gc3RhZ2UgaW4gdGhlIGRhdGEgc2NpZW5jZSB3b3JrZmxvdy4gSXQgaW52b2x2ZXMgZXh0cmFjdGluZyBhIHBvcnRpb24gb2YgdGhlIGRhdGFzZXQgYmFzZWQgb24gc3BlY2lmaWMgY29uZGl0aW9ucy4KOjo6CgpJbiB0aGlzIHNob3J0IHR1dG9yaWFsLCB3ZSB3aWxsIGxlYXJuOgoKMS4gIFdoYXQgc3Vic2V0dGluZyBtZWFucyBhbmQgd2h5IGl0IGlzIGNydWNpYWwKMi4gIEhvdyB0byBwZXJmb3JtIHN1YnNldHRpbmcgdXNpbmcgY29uZGl0aW9uYWwgb3BlcmF0b3JzIGFuZCB0aGUgZHBseXIgZnVuY3Rpb25zIGBmaWx0ZXIoKWAgYW5kIGBzZWxlY3QoKWAKCiMjIyMgV2h5IGlzIHN1YnNldHRpbmcgYSBkYXRhc2V0IGltcG9ydGFudD8KCjo6OiBub3RlCk1vc3QgcmVhbC13b3JsZCBkYXRhc2V0cyBhcmUgbGFyZ2UgYW5kIGNvbXBsZXgsIHdpdGggbWFueSB2YXJpYWJsZXMgYW5kIHRob3VzYW5kcyBvZiByZWNvcmRzLgo6OjoKCk1vc3QgcmVhbC13b3JsZCBkYXRhc2V0cyBhcmUgbGFyZ2UgYW5kIGNvbXBsZXgsIHdpdGggbWFueSB2YXJpYWJsZXMgYW5kIHRob3VzYW5kcyBvZiByZWNvcmRzLiBBbmFseXppbmcgYWxsIG9mIGl0IGF0IG9uY2UgY2FuIGludHJvZHVjZSBub2lzZSwgc2xvdyBkb3duIGNvbXB1dGF0aW9uLCBhbmQgbWFrZSBpdCBoYXJkZXIgdG8gZGV0ZWN0IG1lYW5pbmdmdWwgcGF0dGVybnMuIEJ5IGZpbHRlcmluZyByb3dzIGFuZCBzZWxlY3Rpbmcgc3BlY2lmaWMgY29sdW1ucywgd2UgcmVkdWNlIHRoaXMgY29tcGxleGl0eSwgaW1wcm92ZSBjbGFyaXR5LCBhbmQgY2FuIG1vcmUgZWZmaWNpZW50bHkgdGVzdCBoeXBvdGhlc2VzIG9yIGJ1aWxkIG1vZGVscy4gU3Vic2V0dGluZyBhbHNvIHBsYXlzIGEga2V5IHJvbGUgaW4gZW5zdXJpbmcgZGF0YSBxdWFsaXR5LCBhbGxvd2luZyB1cyB0byByZW1vdmUgaXJyZWxldmFudCBvciBwcm9ibGVtYXRpYyBlbnRyaWVzIGxpa2UgbWlzc2luZyB2YWx1ZXMsIG91dGxpZXJzLCBvciBjYXRlZ29yaWVzIG91dHNpZGUgdGhlIHNjb3BlIG9mIG91ciBhbmFseXNpcy4KCjo6OiBub3RlCkZpbHRlcmluZyByb3dzIGFuZCBzZWxlY3Rpbmcgc3BlY2lmaWMgY29sdW1ucyBoZWxwcyB1czoKCi0gICBSZWR1Y2UgZGF0YXNldCBjb21wbGV4aXR5IGFuZCBpbXByb3ZlIGNsYXJpdHksIG1ha2luZyBpdCBlYXNpZXIgdG8gdGVzdCBoeXBvdGhlc2VzIGVmZmljaWVudGx5Ci0gICBSZW1vdmUgaXJyZWxldmFudCBvciBwcm9ibGVtYXRpYyBlbnRyaWVzIHN1Y2ggYXMgbWlzc2luZyB2YWx1ZXMsIG91dGxpZXJzLCBvciB1bnVzZWQgY2F0ZWdvcmllcywgaW1wcm92aW5nIG92ZXJhbGwgZGF0YSBxdWFsaXR5Cjo6OgoKOjo6IGZsYWcKVGhlIG9yaWdpbmFsIGRhdGFzZXQgaXMgdmVyeSBsYXJnZS0tLWl0IGluY2x1ZGVzIDg0OCB2YXJpYWJsZXMgYW5kIDE3LDAwMCsgb2JzZXJ2YXRpb25zLgo6OjoKClRoZSBkYXRhc2V0IHdlIGhhdmUgdXNlZCBpbiBwcmV2aW91cyBzZXNzaW9ucyBvcmlnaW5hdGVkIGZyb20gdGhlIFtHZW5lcmFsIFNvY2lhbCBTdXJ2ZXksIEN5Y2xlIDI5ICgyMDE1KV0gKGh0dHBzOi8vb2Rlc2kuY2EvZW4vZGV0YWlscz9pZD0vb2Rlc2kvZG9pX18xMC01NjgzX1NQM19SRFMwQ0sueG1sKSwgZnJvbSB0aGUgU29jaWFsIGFuZCBBYm9yaWdpbmFsIFN0YXRpc3RpY3MgRGl2aXNpb24gYXQgU3RhdGlzdGljcyBDYW5hZGEuIFRoaXMgc3VydmV5IHRyYWNrcyBob3cgQ2FuYWRpYW5zIHNwZW5kIGFuZCBtYW5hZ2UgdGhlaXIgdGltZSwgaGVscGluZyB1cyB1bmRlcnN0YW5kIHBhdHRlcm5zIHRpZWQgdG8gd2VsbC1iZWluZyBhbmQgc3RyZXNzLiBIb3dldmVyLCB0aGUgb3JpZ2luYWwgZGF0YXNldCBpcyB2ZXJ5IGxhcmdlLS0taXQgaW5jbHVkZXMgb3ZlciA4NDggdmFyaWFibGVzIGFuZCBtb3JlIHRoYW4gMTcsMDAwIG9ic2VydmF0aW9ucy4KCkZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyB0dXRvcmlhbCwgd2UgYXJlIG5vdCBpbnRlcmVzdGVkIGluIGV2ZXJ5IHZhcmlhYmxlLiBJbnN0ZWFkLCB3ZSBhcmUgd29ya2luZyB3aXRoIGEgKipzdWJzZXQqKiBvZiB0aGlzIGRhdGFzZXQ6IDI5IHZhcmlhYmxlcyBmb2N1c2VkIG1haW5seSBvbiB0aW1lIGR1cmF0aW9ucyBhbmQga2V5IGRlbW9ncmFwaGljIGNoYXJhY3RlcmlzdGljcy4gVGhpcyBtYWtlcyB0aGUgZGF0YSBtb3JlIG1hbmFnZWFibGUgYW5kIHJlbGV2YW50IGZvciBvdXIgZXhwbG9yYXRpb24gb2YgdGltZSB1c2UgYW5kIHBlcmNlcHRpb25zIG9mIHRpbWUgcHJlc3N1cmUuCgojIyMjIEZyYW1pbmcgdGhlIEd1aWRpbmcgUXVlc3Rpb246IFdobyBGZWVscyBSdXNoZWQ/CjwhLS0gIyMjIEZyYW1pbmcgdGhlIEd1aWRpbmcgUXVlc3Rpb246IFdobyBGZWVscyBSdXNoZWQ/IC0tPgoKTGV0J3MgcmV0dXJuIHRvIHRoZSB0aW1lIHVzYWdlIGRhdGFzZXQuIEluIHRoaXMgc2VjdGlvbiwgc3VwcG9zZSB3ZSdyZSBpbnRlcmVzdGVkIGluIHVuZGVyc3RhbmRpbmcgaG93IHBlb3BsZSB3aG8gZmVlbCBydXNoZWQgc3BlbmQgdGhlaXIgdGltZSBkaWZmZXJlbnRseSBmcm9tIHRob3NlIHdobyBkb24ndC4gVG8gYW5zd2VyIHRoaXMgcXVlc3Rpb24sIHdlIGRvbid0IG5lZWQgZXZlcnkgc2luZ2xlIHJvdyBvciBjb2x1bW4sIGFzIHdvcmtpbmcgd2l0aCB0aGUgZGF0YSBpbiBpdHMgb3JpZ2luYWwgZm9ybWF0IHdvdWxkIGJlIHVubmVjZXNzYXJpbHkgY29tcGxleC4gSW5zdGVhZCwgd2UgbmVlZCB0byAiZ2V0IGluc2lkZSIgb3VyIGRhdGEgYnkgc3Vic2V0dGluZzogZmlsdGVyaW5nIHRoZSByb3dzIGFuZCBzZWxlY3RpbmcgdGhlIGNvbHVtbnMgdGhhdCBtYXR0ZXIuCgpXZSB3aWxsIGV4cGxvcmUgb3VyIGRhdGFzZXQgdGhyb3VnaCBvbmUgZ3VpZGluZyBxdWVzdGlvbjoKCioqSG93IGRvIHBlb3BsZSB3aG8gZmVlbCBydXNoZWQgc3BlbmQgdGhlaXIgdGltZSBkaWZmZXJlbnRseSBmcm9tIHRob3NlIHdobyBkb24ndD8qKgoKV2UnbGwgZm9jdXMgb24gdGhlIHJlbGV2YW50IHJvd3MgYW5kIGNvbHVtbnMgdGhhdCBhbnN3ZXIgdGhpcyBxdWVzdGlvbi4KCiMjIFN0ZXAgYnkgU3RlcAoKIyMjIDA6IFNldHVwIGFuZCBMb2FkIHRoZSBEYXRhCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UpCmBgYAoKIyMjIyBMb2FkIGFuZCBVbmRlcnN0YW5kIHRoZSBEYXRhc2V0Cgo8IS0tICMjIyBMb2FkIGFuZCBVbmRlcnN0YW5kIHRoZSBEYXRhc2V0ICAtLT4KCkZpcnN0LCB3ZSBuZWVkIHRvIGxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXMgZm9yIHRoaXMgc2Vzc2lvbi4KCmBgYHtyLCBkYXRhLWlzb2xhdGlvbi0xNSwgcmVzdWx0cyA9ICdoaWRlJywgZWNobz1GQUxTRX0KbGlicmFyeShrYWJsZUV4dHJhKQpgYGAKCmBgYHtyLCBsaWJyYXJpZXN9CmxpYnJhcnkoZHBseXIpCmBgYAoKSW4gb3VyIHN1YnNlcXVlbnQgdGFza3MsIHRoZSBgZHBseXJgIHBhY2thZ2Ugd2lsbCBiZSBlc3NlbnRpYWwgZm9yIHN1YnNldHRpbmcgb3BlcmF0aW9ucyBzdWNoIGFzIGZpbHRlcmluZyByb3dzIGFuZCBzZWxlY3RpbmcgY29sdW1ucy4gV2UgY2FuIGVpdGhlciBjb250aW51ZSB1c2luZyB0aGUgYGpzX2RhdGFgIG9iamVjdCBmcm9tIHRoZSBwcmV2aW91cyBzZWN0aW9uIG9yIGxvYWQgdGhlIGB0aW1ldXNlX2RheTNfMS5SZGF0YWAgZmlsZSBmcm9tIHRoZSBkYXRhIGZvbGRlci4KCmBgYHtyfQpqc19kYXRhX3BhdGggPC0gImRhdGEvdGltZXVzZV9kYXkzXzEuUmRhdGEiCmxvYWQoanNfZGF0YV9wYXRoKQpgYGAKCk5vdywgbGV0J3MgYWdhaW4gZXhhbWluZSBvdXIgZGF0YXNldCBzdHJ1Y3R1cmUgYnkgZGlzcGxheWluZyB0aGUgZmlyc3QgZmV3IHJvd3MgYnkgdXNpbmcgYGhlYWQoKWAgZnVuY3Rpb24uCgpgYGB7ciwgZGF0YS1pc29sYXRpb24tMSwgcmVzdWx0cyA9ICdoaWRlJ30KanNfZGF0YSB8PgogIGhlYWQoKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpqc19kYXRhIHw+CiAgaGVhZCgpIHw+CiAga2JsKCkgfD4KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PgogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKQXMgd2UgY2FuIHNlZSwgdGhlcmUgYXJlIDMwIGNvbHVtbnMgaW4gdGhlIGRhdGFzZXQsIHdoaWNoIGlzIGEgbG90IHRvIHdvcmsgd2l0aC4gRm9yIG5vdywgd2UgY2FuIGZvY3VzIG9uIHRoZSBmb2xsb3dpbmcga2V5IGNvbHVtbnM6CgotICAgYGlkYDogUmVjb3JkIGlkZW50aWZpY2F0aW9uCi0gICBgYWdlR3JwYDogQWdlIGdyb3VwIG9mIHJlc3BvbmRlbnQgKGdyb3VwcyBvZiAxMCkKLSAgIGBzZXhgOiBTZXggb2YgcmVzcG9uZGVudAotICAgYG1hcml0YWxTdGF0YDogTWFyaXRhbCBzdGF0dXMgb2YgdGhlIHJlc3BvbmRlbnQKLSAgIGBwcm92aW5jZWA6IFByb3ZpbmNlIG9mIHJlc2lkZW5jZQotICAgYHBvcENlbnRlcmA6IFBvcHVsYXRpb24gY2VudHJlIGluZGljYXRvcgotICAgYGVkdUxldmVsYDogRWR1Y2F0aW9uYWwgYXR0YWlubWVudCAoaGlnaGVzdCBkZWdyZWUpCi0gICBgZmVlbFJ1c2hlZGA6IEdlbmVyYWwgdGltZSB1c2UgLS0gRmVlbCBydXNoZWQKLSAgIGBleHRyYVRpbWVgOiBHZW5lcmFsIHRpbWUgdXNlIC0tIEV4dHJhIHRpbWUKLSAgIGBkdXJTbGVlcGA6IER1cmF0aW9uIC0tIFNsZWVwaW5nLCByZXN0aW5nLCByZWxheGluZywgc2ljayBpbiBiZWQKLSAgIGBkdXJXb3JrYDogRHVyYXRpb24gLS0gUGFpZCB3b3JrCi0gICBgdGltZVdvcmthaG9saWNgOiBQZXJjZXB0aW9ucyBvZiB0aW1lIC0tIFdvcmthaG9saWMKLSAgIGB0aW1lV2FudEFsb25lYDogUGVyY2VwdGlvbnMgb2YgdGltZSAtLSBXb3VsZCBsaWtlIG1vcmUgdGltZSBhbG9uZQoKVGhlc2UgY29sdW1ucyBwcm92aWRlIGluZm9ybWF0aW9uIG9uIGRlbW9ncmFwaGljcywgdGltZSB1c2FnZSwgYW5kIHRpbWUgcGVyY2VwdGlvbnMuIFdlIGNhbiB1c2UgdGhlbSB0byBleHBsb3JlIHBhdHRlcm5zIGluIHdvcmstbGlmZSBiYWxhbmNlLCBlZHVjYXRpb24sIGFuZCBzb2NpYWwgdGltZS4KCk5vdyB0aGF0IHdlIHVuZGVyc3RhbmQgb3VyIGRhdGFzZXQgc3RydWN0dXJlLCBsZXQncyBtb3ZlIG9uIHRvIGxlYXJuaW5nIGhvdyB0byBmaWx0ZXIgYW5kIG1hbmlwdWxhdGUgdGhpcyBkYXRhIGVmZmVjdGl2ZWx5IHVzaW5nIEJvb2xlYW4gb3BlcmF0b3JzIGluIGBkcGx5cmAuCgoKIyMjIDE6IExlYXJuaW5nIEFib3V0IEZpbHRlcmluZwoKIyMjIyBDb25kaXRpb25hbCBGaWx0ZXJpbmcgd2l0aCBCb29sZWFuIE9wZXJhdG9ycyB1c2luZyA8c3Ryb25nPmRwbHlyPC9zdHJvbmc+Cgo8IS0tICMjIyBDb25kaXRpb25hbCBGaWx0ZXJpbmcgd2l0aCBCb29sZWFuIE9wZXJhdG9ycyB1c2luZyAqKmRwbHlyKiogLS0+CgpgZHBseXJgIGlzIGEgcG93ZXJmdWwgcGFja2FnZSB0aGF0IGxldHMgdXMgZXh0cmFjdCBhbmQgdHJhbnNmb3JtIGRhdGEgd2l0aCBhIGNsZWFyLCByZWFkYWJsZSBzeW50YXguIEluIGBkcGx5cmAsIHdlIHVzZSBmdW5jdGlvbnMgbGlrZSBgZmlsdGVyKClgLCBgc2VsZWN0KClgLCBhbmQgYG11dGF0ZSgpYCB0byB3b3JrIHdpdGggb3VyIGRhdGEuIEJvb2xlYW4gb3BlcmF0b3JzIChgPT1gLCBgPGAsIGA+YCwgYDw9YCwgYD49YCwgYW5kIGAhPWApIGFyZSB1c2VkIHdpdGhpbiB0aGVzZSBmdW5jdGlvbnMgdG8gdGVzdCBjb25kaXRpb25zLCBhbmQgd2UgY2FuIGNvbWJpbmUgY29uZGl0aW9ucyB3aXRoIGAmYCAoYW5kKSBvciBgfGAgKG9yKS4KCkxldCdzIHN0YXJ0IGJ5IHVuZGVyc3RhbmRpbmcgdGhlIGJhc2ljIGNvbXBhcmlzb24gb3BlcmF0b3JzIHRoYXQgd2lsbCBoZWxwIHVzIGNyZWF0ZSBmaWx0ZXJpbmcgY29uZGl0aW9ucy4KCioqQ29tcGFyaXNvbiBPcGVyYXRvcnMqKgoKQ29tcGFyaXNvbiBvcGVyYXRvcnMgYWxsb3cgdXMgdG8gY2hlY2sgY29uZGl0aW9ucyB3aXRoaW4gb3VyIGRhdGFzZXQuIFRoZXNlIHJldHVybiBgVFJVRWAgb3IgYEZBTFNFYCBiYXNlZCBvbiB3aGV0aGVyIHRoZSBjb25kaXRpb24gaXMgbWV0LgoKOjo6bWQtdGFibGUKfCBPcGVyYXRvciB8IE1lYW5pbmcgICAgICAgICAgICAgICAgICB8IEV4YW1wbGUgIHwgUmVzdWx0IHwKfC0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLXwtLS0tLS0tLXwKfCBgPT1gICAgICB8IEVxdWFsIHRvICAgICAgICAgICAgICAgICB8IGA1ID09IDVgIHwgYFRSVUVgIHwKfCBgIT1gICAgICB8IE5vdCBlcXVhbCB0byAgICAgICAgICAgICB8IGA1ICE9IDNgIHwgYFRSVUVgIHwKfCBgPGAgICAgICB8IExlc3MgdGhhbiAgICAgICAgICAgICAgICB8IGAzIDwgNWAgIHwgYFRSVUVgIHwKfCBgPmAgICAgICB8IEdyZWF0ZXIgdGhhbiAgICAgICAgICAgICB8IGA1ID4gM2AgIHwgYFRSVUVgIHwKfCBgPD1gICAgICB8IExlc3MgdGhhbiBvciBlcXVhbCB0byAgICB8IGAzIDw9IDNgIHwgYFRSVUVgIHwKfCBgPj1gICAgICB8IEdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byB8IGA1ID49IDNgIHwgYFRSVUVgIHwKOjo6CgpPbmNlIHdlIHVuZGVyc3RhbmQgdGhlc2UgYmFzaWMgY29tcGFyaXNvbiBvcGVyYXRvcnMsIHdlIGNhbiBjb21iaW5lIHRoZW0gdXNpbmcgbG9naWNhbCBvcGVyYXRvcnMgdG8gY3JlYXRlIG1vcmUgY29tcGxleCBmaWx0ZXJpbmcgY29uZGl0aW9ucy4KCioqTG9naWNhbCBPcGVyYXRvcnMqKgoKTG9naWNhbCBvcGVyYXRvcnMgYWxsb3cgdXMgdG8gZmlsdGVyIGRhdGEgYmFzZWQgb24gbXVsdGlwbGUgY29uZGl0aW9ucy4KCjo6Om1kLXRhYmxlCnwgT3BlcmF0b3IgfCBNZWFuaW5nICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBFeGFtcGxlICAgICAgICAgICAgIHwgUmVzdWx0ICB8CnwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tfAp8IGAmYCAgICAgIHwgTG9naWNhbCBBTkQgKEJvdGggY29uZGl0aW9ucyBtdXN0IGJlIFRSVUUpICAgICAgIHwgYCg1ID4gMykgJiAoNCA8IDYpYCB8IGBUUlVFYCAgfAp8IGB8YCAgICAgIHwgTG9naWNhbCBPUiAoQXQgbGVhc3Qgb25lIGNvbmRpdGlvbiBtdXN0IGJlIFRSVUUpIHwgYCg1ID4gMykgfCAoNCA+IDYpYCB8IGBUUlVFYCAgfAp8IGAhYCAgICAgIHwgTG9naWNhbCBOT1QgKFJldmVyc2VzIFRSVUUvRkFMU0UpICAgICAgICAgICAgICAgIHwgYCEoNSA+IDMpYCAgICAgICAgICB8IGBGQUxTRWAgfAo6OjoKCkxldCdzIHRyeSB1c2luZyB0aGVzZSBsb2dpY2FsIG9wZXJhdG9ycyB0byBmaWx0ZXIgdGhlIHJvd3MgaW4gdGhlIGZvbGxvd2luZyBleGFtcGxlLgoKV2Ugd2FudCB0byB1bmRlcnN0YW5kIHdoZXRoZXIgZmVlbGluZyBydXNoZWQgbWlnaHQgcmVsYXRlIHRvIGhvdyBtdWNoIHRpbWUgaXMgc3BlbnQgb24gd29yaywgc2xlZXAsIG9yIGFsb25lIHRpbWUuIFRoZXJlZm9yZSwgdGhlIGNvbHVtbnMgb2YgaW50ZXJlc3QgYXJlIGBmZWVsUnVzaGVkYCwgYGR1clNsZWVwYCwgYGR1cldvcmtgLCBhbmQgYGR1ckFsb25lYC4gRmlyc3QsIGxldCdzIGV4cGxvcmUgdGhlIHZhbHVlcyBpbiB0aGVzZSBjb2x1bW5zIHVzaW5nIGBkcGx5cmAuCgpMZXQncyBsb29rIGF0IHRoZSB1bmlxdWUgdmFsdWVzIGluIHRoZSBgZmVlbFJ1c2hlZGAgY29sdW1uIHRvIHVuZGVyc3RhbmQgd2hhdCBjYXRlZ29yaWVzIGV4aXN0IGluIG91ciBkYXRhIGJlZm9yZSB3ZSBzdGFydCBmaWx0ZXJpbmcgYmFzZWQgb24gdGhlc2UgdmFsdWVzLgoKYGBge3J9CmpzX2RhdGEgfD4gCiAgZGlzdGluY3QoZmVlbFJ1c2hlZCkKYGBgCgpTaW5jZSBvdXIgZHVyYXRpb24gY29sdW1ucyAoYGR1clNsZWVwYCwgYGR1cldvcmtgLCBhbmQgYGR1ckFsb25lYCkgYXJlIGNvbnRpbnVvdXMgbnVtZXJpY2FsIHZhcmlhYmxlcyByYXRoZXIgdGhhbiBjYXRlZ29yaWNhbCwgaXQncyBtb3JlIGluZm9ybWF0aXZlIHRvIGV4YW1pbmUgdGhlaXIgZGlzdHJpYnV0aW9ucyByYXRoZXIgdGhhbiBqdXN0IHRoZWlyIHVuaXF1ZSB2YWx1ZXMuIExvb2tpbmcgYXQgdGhlIGRpc3RyaWJ1dGlvbiBoZWxwcyB1cyB1bmRlcnN0YW5kIHdoaWNoIHZhbHVlcyBhcmUgY29tbW9uLCBpZGVudGlmeSBwYXR0ZXJucywgYW5kIHNwb3QgcG90ZW50aWFsIG91dGxpZXJzOgoKYGBge3J9CmpzX2RhdGEgfD4gCiAgY291bnQoZHVyU2xlZXApIHw+IAogIGFycmFuZ2UoZGVzYyhuKSkgfD4gCiAgaGVhZCgxMCkgCmBgYAoKYGBge3J9CmpzX2RhdGEgfD4gCiAgY291bnQoZHVyV29yaykgfD4gCiAgYXJyYW5nZShkZXNjKG4pKSB8PiAKICBoZWFkKDEwKQpgYGAKCmBgYHtyfQpqc19kYXRhIHw+IAogIGNvdW50KGR1ckFsb25lKSB8PiAKICBhcnJhbmdlKGRlc2MobikpIHw+IAogIGhlYWQoMTApIApgYGAKCjo6OiB3YWxrdGhyb3VnaApUaGVzZSBjb21tYW5kcyB3b3JrIHRvZ2V0aGVyIHRvIHNob3cgdGhlIG1vc3QgY29tbW9uIHRpbWUgcGF0dGVybnM6CgotICAgYGNvdW50KClgIHRhbGxpZXMgZWFjaCBkdXJhdGlvbiB2YWx1ZSwKLSAgIGBhcnJhbmdlKGRlc2MobikpYCBzb3J0cyBmcm9tIGhpZ2hlc3QgdG8gbG93ZXN0IGZyZXF1ZW5jeSwKLSAgIGBoZWFkKDEwKWAga2VlcHMgb25seSB0aGUgdG9wIDEwIHJlc3VsdHMKClRoaXMgcXVpY2sgb3ZlcnZpZXcgaGVscHMgdXMgdW5kZXJzdGFuZCB0eXBpY2FsIHRpbWUgYWxsb2NhdGlvbiBwYXR0ZXJucyBmb3Igc2xlZXAsIHdvcmssIGFuZCBiZWluZyBhbG9uZS4KOjo6CgpJdCBhcHBlYXJzIHRoYXQgYWxsIHRoZSBjb2x1bW5zIGNvbnRhaW4gbnVtZXJpYyB2YWx1ZXMuIEhvd2V2ZXIsIGFzIHdlIGxvb2sgYXQgdGhlIGRhdGEgZGljdGlvbmFyeSwgd2UnbGwgc2VlIHRoYXQgb25seSBgZHVyV29ya2AsIGBkdXJTbGVlcGAgYW5kIGBkdXJBbG9uZWAgaGF2ZSBudW1lcmljIHZhbHVlcyB0aGF0IHJlcHJlc2VudCByZWFsIHF1YW50aXRpZXMgKG1pbnV0ZXMgb2Ygd29yaywgc2xlZXAgb3IgdGltZSBhbG9uZSBpbiBhIGRheSkuIEluIGNvbnRyYXN0LCB0aGUgdmFsdWVzIGluIHRoZSBgZmVlbFJ1c2hlZGAgY29sdW1uIGhhdmUgYSBkaWZmZXJlbnQgbWVhbmluZy4KCjo6Om1kLXRhYmVsCnwgQ29kZSB8IFZhbHVlICAgICAgICAgICAgfAp8LS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwKfCAxICAgIHwgZGFpbHkgICAgICAgICAgICB8CnwgMiAgICB8IGZldyBUaW1lcyBhIFdlZWsgfAp8IDMgICAgfCBvbmNlIGEgV2VlayAgICAgIHwKfCA0ICAgIHwgb25jZSBhIE1vbnRoICAgICB8CnwgNSAgICB8IGxlc3MgYSBNb250aCAgICAgfAp8IDYgICAgfCBuZXZlciAgICAgICAgICAgIHwKOjo6CgojIyMgMjogQXBwbHkgQmFzaWMgRmlsdGVyaW5nCgo8IS0tICMjIyBGaWx0ZXJpbmcgUm93cyB3aXRoICpkcGx5ciogLS0+CgojIyMjIEZpbHRlcmluZyBSb3dzIHdpdGggPHN0cm9uZz5kcGx5cjwvc3Ryb25nPgoKIVtdKGltYWdlcy8zLUFDVC0zLUZpbHRlci5wbmcpCgpGb3IgdGhpcyBhbmFseXNpcywgd2Ugd2lsbCBjb25zaWRlciByZXNwb25kZW50cyB3aG8gcmVwb3J0IGZlZWxpbmcgcnVzaGVkIGFzIHRob3NlIHdob3NlIGZyZXF1ZW5jeSBvZiBmZWVsaW5nIHRoaXMgd2F5IGlzIGF0IGxlYXN0IG9uY2UgYSB3ZWVrLCBhbmQgdGhvc2Ugd2hvIGZlZWwgdGhpcyB3YXkgbGVzcyB0aGFuIG9uY2UgYSB3ZWVrIGFzIG5vdCBmZWVsaW5nIHJ1c2hlZC4KCiMjIyMgRXh0cmFjdGluZyBSZXNwb25kZW50cyBXaG8gRG8gb3IgRG8gbm90IEZlZWwgUnVzaGVkIERhaWx5CgpGaXJzdCwgbGV0J3MgZXh0cmFjdCBvbmx5IHRoZSByZXNwb25kZW50cyB3aG8gcmVwb3J0IGZlZWxpbmcgcnVzaGVkIGRhaWx5LiBMb29raW5nIGF0IHRoZSB0YWJsZSwgdGhlIHZhbHVlIGNvcnJlc3BvbmRpbmcgdG8gZmVlbGluZyBydXNoZWQgZGFpbHkgaXMgMS4gV2UgY2FuIGZpbHRlciBmb3IgdGhlc2UgcmVzcG9uZGVudHMgdXNpbmcgYGZpbHRlcigpYC4KCjo6OiBub3RlClRoZSBgZmlsdGVyKClgIGZ1bmN0aW9uIGluIGBkcGx5cmAgcmV0dXJucyBvbmx5IHRoZSByb3dzIHRoYXQgbWVldCBhIHNwZWNpZmllZCBjb25kaXRpb24uCjo6OgoKV2UgZmlsdGVyIGFueSByb3cgd2hpY2ggaGFzIGBmZWVsUnVzaGVkYCBlcXVhbCB0byAxIGJ5IHBhc3NpbmcgdGhlIEJvb2xlYW4gZXhwcmVzc2lvbiBgZmVlbFJ1c2hlZCA9PSAxYCBpbnRvIGBmaWx0ZXJgLCBhbmQgdGhlbiB1c2UgYGhlYWQoKWAgdG8gc2VlIHdoYXQgdGhlIGZpbHRlcmVkIGRhdGEgbG9vayBsaWtlLgoKYGBge3IsIGRhdGEtaXNvbGF0aW9uLTIsIHJlc3VsdHMgPSAnaGlkZSd9CmpzX2RhdGEgfD4gCiAgZmlsdGVyKGZlZWxSdXNoZWQgPT0gMSkgfD4gCiAgaGVhZCgpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmpzX2RhdGEgfD4gCiAgZmlsdGVyKGZlZWxSdXNoZWQgPT0gMSkgfD4gCiAgaGVhZCgpIHw+CiAga2JsKCkgfD4KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PgogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKTG9va2luZyBhdCB0aGUgZmlsdGVyZWQgZGF0YSwgd2UgY2FuIGNvbmZpcm0gdGhhdCBvdXIgZmlsdGVyIHdvcmtlZCBjb3JyZWN0bHktLS1hbGwgcm93cyBzaG93IGBmZWVsUnVzaGVkYCBlcXVhbCB0byAxIChtZWFuaW5nIHRoZXkgZmVlbCBydXNoZWQgZGFpbHkpLgoKVXNpbmcgYGZpbHRlcigpYCwgd2Ugc3Vic2V0IHRoZSBkYXRhIHRvIGtlZXAgb25seSB0aG9zZSByb3dzIHdoZXJlIHRoZSBjb25kaXRpb24gaXMgbWV0LiBJbiB0aGUgYWJvdmUgZXhhbXBsZSwgdGhlIGNvbmRpdGlvbiBgZmVlbFJ1c2hlZCA9PSAxYCBjcmVhdGVzIGEgbG9naWNhbCB2ZWN0b3IgdGhhdCBpcyBgVFJVRWAgZm9yIHJvd3Mgd2hlcmUgdGhlIHZhbHVlIGVxdWFscyAxIChjb3JyZXNwb25kaW5nIHRvICJkYWlseSIpLgoKIyMjIyBVc2luZyBDb21wYXJpc29uIGFuZCBMb2dpY2FsIE9wZXJhdG9ycwoKTm90IG9ubHkgY2FuIHdlIHVzZSB0aGUgYD09YCBvcGVyYXRvciB0byB0ZXN0IGZvciBlcXVhbGl0eSwgYnV0IHdlIGNhbiBhbHNvIHVzZSBvcGVyYXRvcnMgc3VjaCBhcyBgPGAsIGA+YCwgYDw9YCwgYD49YCwgYW5kIGAhPWAgdG8gY29tcGFyZSB2YWx1ZXMuIEZvciBleGFtcGxlLCB3ZSBtaWdodCBmaWx0ZXIgcm93cyB3aGVyZSBhIG51bWVyaWMgdmFyaWFibGUgZXhjZWVkcyBhIGNlcnRhaW4gdGhyZXNob2xkLCBpcyBiZWxvdyBhIGxpbWl0LCBvciBpcyBub3QgZXF1YWwgdG8gYSBzcGVjaWZpZWQgdmFsdWUuIExldCdzIHVzZSBhbiBleGFtcGxlIGZyb20gdGhlIGBkdXJTbGVlcGAgY29sdW1uLgoKRm9yIGluc3RhbmNlLCBpZiB3ZSB3YW50IHRvIGZpbHRlciByb3dzIGZvciB0aGUgYGR1clNsZWVwYCBjb2x1bW4gdG8gY2FwdHVyZSBhbnkgaW5zdGFuY2VzIHdpdGggc2xlZXAgZHVyYXRpb24gbGVzcyB0aGFuIGA2MDBgLCB3ZSBjYW4gd3JpdGU6CgpgYGB7ciwgZGF0YS1pc29sYXRpb24tMywgcmVzdWx0cyA9ICdoaWRlJ30KanNfZGF0YSB8PiAKICBmaWx0ZXIoZHVyU2xlZXAgPCA2MDApIHw+CiAgaGVhZCgpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmpzX2RhdGEgfD4gCiAgZmlsdGVyKGR1clNsZWVwIDwgNjAwKSB8PgogIGhlYWQoKSB8PgogIGtibCgpIHw+CiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQpgYGAKCkFsdGVybmF0aXZlbHksIHdlIGNhbiBmaWx0ZXIgcm93cyB3aGVyZSBgZHVyU2xlZXBgIGlzIGJldHdlZW4gYDYwMGAgYW5kIGAxMDAwYC4gVG8gZG8gdGhpcywgd2UgY2hhaW4gdHdvIGNvbmRpdGlvbnMgdXNpbmcgdGhlIGAmYCBvcGVyYXRvcjoKCmBgYHtyLCBkYXRhLWlzb2xhdGlvbi00LCByZXN1bHRzID0gJ2hpZGUnfQpqc19kYXRhIHw+IAogIGZpbHRlcihkdXJTbGVlcCA+PSA2MDAgJiBkdXJTbGVlcCA8PSAxMDAwKSB8PgogIGhlYWQoKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpqc19kYXRhIHw+IAogIGZpbHRlcihkdXJTbGVlcCA+PSA2MDAgJiBkdXJTbGVlcCA8PSAxMDAwKSB8PgogIGhlYWQoKSB8PgogIGtibCgpIHw+CiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQpgYGAKCjo6OiB3YWxrdGhyb3VnaApJbiB0aGlzIGV4YW1wbGU6CgotICAgVGhlIGNvbmRpdGlvbiBgZHVyU2xlZXAgPj0gNjAwYCBjaGVja3MgZm9yIHJvd3Mgd2hlcmUgc2xlZXAgZHVyYXRpb24gaXMgYXQgbGVhc3QgYDYwMGAuCi0gICBUaGUgY29uZGl0aW9uIGBkdXJTbGVlcCA8PSAxMDAwYCBjaGVja3MgZm9yIHJvd3Mgd2hlcmUgc2xlZXAgZHVyYXRpb24gaXMgYXQgbW9zdCBgMTAwMGAuCi0gICBUaGUgYCZgIG9wZXJhdG9yIGNvbWJpbmVzIHRoZXNlIGNvbmRpdGlvbnMsIGVuc3VyaW5nIHRoYXQgb25seSByb3dzIHNhdGlzZnlpbmcgYm90aCBjb25kaXRpb25zIGFyZSByZXR1cm5lZC4KOjo6CgpIZXJlIHdlJ3ZlIHVzZWQgdGhlIGAmYCBvcGVyYXRvciBmb3IgImFuZCIgY29uZGl0aW9ucywgYnV0IHdlIGNhbiBhbHNvIHVzZSB0aGUgYHxgIG9wZXJhdG9yIHRvIHNwZWNpZnkgIm9yIiBjb25kaXRpb25zLiBCeSB1c2luZyB0aGVzZSBib29sZWFuIG9wZXJhdG9ycywgd2UgY2FuIGNoYWluIG11bHRpcGxlIGNvbmRpdGlvbnMgdG9nZXRoZXIuIAoKIyMjIDM6IEFwcGx5IENvbXBsZXggRmlsdGVyaW5nCgo8IS0tICMjIyBDb21wbGV4IEZpbHRlcmluZyB3aXRoIGRwbHlyOiBGaWx0ZXJpbmcgUm93cyBmb3IgVGhvc2UgV2hvIEZlZWwgUnVzaGVkIC0tPgoKIyMjIyBDb21wbGV4IEZpbHRlcmluZyB3aXRoIDxzdHJvbmc+ZHBseXI8L3N0cm9uZz46IEZpbHRlcmluZyBSb3dzIGZvciBUaG9zZSBXaG8gRmVlbCBSdXNoZWQKCk5vdywgbGV0J3MgcGVyZm9ybSBhIG1vcmUgY29tcGxleCBmaWx0ZXJpbmcuIFN1cHBvc2Ugd2Ugd2FudCB0byBjYXB0dXJlIHJlc3BvbmRlbnRzIHdobyBmZWVsIHJ1c2hlZCBmcmVxdWVudGx5LS0tdGhhdCBpcywgdGhvc2Ugd2hvc2UgYGZlZWxSdXNoZWRgIHZhbHVlIGlzIGVpdGhlciBgMWAsIGAyYCwgb3IgYDNgLS0tYW5kIHRob3NlIHdobyBkbyBub3QgZmVlbCBydXNoZWQgZnJlcXVlbnRseSwgbWVhbmluZyB0aG9zZSB3aG9zZSBgZmVlbFJ1c2hlZGAgdmFsdWUgaXMgZWl0aGVyIGA0YCwgYDVgLCBvciBgNmAuCgpUaGVyZSBhcmUgbXVsdGlwbGUgd2F5cyB0byBmaWx0ZXIgcm93cyB0aGF0IG1lZXQgb25lIG9mIHRoZXNlIGNvbmRpdGlvbnMuCgojIyMjIFVzaW5nIFJhbmdlIENvbXBhcmlzb24KClRoZSBmaXJzdCBtZXRob2QgdXNlcyBhIHJhbmdlIGNvbXBhcmlzb24gd2l0aCBgPD1gIHRvIGZpbHRlciB0aGUgcm93cy4KCmBgYHtyLCBkYXRhLWlzb2xhdGlvbi01LCByZXN1bHRzID0gJ2hpZGUnfQpqc19kYXRhIHw+IAogIGZpbHRlcihmZWVsUnVzaGVkIDw9IDMpIHw+CiAgaGVhZCgpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmpzX2RhdGEgfD4gCiAgZmlsdGVyKGZlZWxSdXNoZWQgPD0gMykgfD4KICBoZWFkKCkgfD4KICBrYmwoKSB8PgogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+CiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikKCmBgYAoKYGBge3IsIGRhdGEtaXNvbGF0aW9uLTYsIHJlc3VsdHMgPSAnaGlkZSd9CmpzX2RhdGEgfD4gCiAgZmlsdGVyKGZlZWxSdXNoZWQgPiAzICYgZmVlbFJ1c2hlZCA8PSA2KSB8PgogIGhlYWQoKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpqc19kYXRhIHw+IAogIGZpbHRlcihmZWVsUnVzaGVkID4gMyAmIGZlZWxSdXNoZWQgPD0gNikgfD4KICBoZWFkKCkgfD4KICBrYmwoKSB8PgogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+CiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikKYGBgCgpOb3cgbGV0J3MgY2FsY3VsYXRlIGhvdyBtYW55IHJvd3MgcmVtYWluIGFmdGVyIHdlIHBlcmZvcm1lZCBmaWx0ZXJpbmcuCgpgYGB7cn0KYWxsX3Jvd3MgPC0ganNfZGF0YSB8PiAKICBucm93KCkKcnVzaGVkX3Jvd3MgPC0ganNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCA8PSAzKSB8PgogIG5yb3coKQpub3RfcnVzaGVkX3Jvd3MgPC0ganNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCA+IDMgJiBmZWVsUnVzaGVkIDw9IDYpIHw+CiAgbnJvdygpCmBgYAoKTGV0J3MgcHJpbnQgYW5kIHNlZSB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gZWFjaCBkYXRhZnJhbWUgYWZ0ZXIgZmlsdGVyaW5nLgoKYGBge3J9CnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHJvd3MgaW4gZGF0YSBpczoiLCBhbGxfcm93cykpCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHJvd3MgaW4gcnVzaGVkIGlzOiIsIHJ1c2hlZF9yb3dzKSkKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2Ygcm93cyBpbiBub3QgcnVzaGVkIGlzOiIsIG5vdF9ydXNoZWRfcm93cykpCmBgYAoKOjo6IHdhbGt0aHJvdWdoCi0gICBUaGlzIGFwcHJvYWNoIHNlbGVjdHMgcm93cyB3aGVyZSBgZmVlbFJ1c2hlZGAgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIGAzYCAoaS5lLiwgdmFsdWVzIGAxYCwgYDJgLCBvciBgM2ApIHRvIGluZGljYXRlIHJlc3BvbmRlbnRzIHdobyBmZWVsIHJ1c2hlZC4KLSAgIFJvd3Mgd2hlcmUgYGZlZWxSdXNoZWRgIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byBgNGAgYW5kIGxlc3MgdGhhbiBvciBlcXVhbCB0byBgNmAgYXJlIGNvbnNpZGVyZWQgbm90IHJ1c2hlZC4KLSAgIFdlIHVzZSB0aGUgYCZgIG9wZXJhdG9yIHRvIGNoYWluIHRoZSB0d28gQm9vbGVhbiBvcGVyYXRpb25zLgotICAgVGhlIGBucm93KClgIGZ1bmN0aW9uIGNhbGN1bGF0ZXMgdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBkYXRhIGZyYW1lLgo6OjoKCkFzIHdlIGNhbiBzZWUsIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lIGhhcyBgMTcsMzkwYCByb3dzOyBhZnRlciBmaWx0ZXJpbmcsIHRoZSBydXNoZWQgZGF0YSBmcmFtZSBjb250YWlucyBvbmx5IGAxMiw2ODlgIHJvd3MuCgojIyMjIENoYWluaW5nIE11bHRpcGxlIENvbmRpdGlvbnMKCkFsdGVybmF0aXZlbHksIHdlIGNhbiB1c2UgYSBzZWNvbmQgbWV0aG9kIHRoYXQgaW52b2x2ZXMgY2hhaW5pbmcgdGhyZWUgY29uZGl0aW9ucyB1c2luZyB0aGUgYHxgIG9wZXJhdG9yLiBXaGlsZSB0aGlzIGFwcHJvYWNoIGlzIG1vcmUgdmVyYm9zZSwgaXQgbWFrZXMgdGhlIGxvZ2ljIHZlcnkgZXhwbGljaXQ6CgpgYGB7ciwgZGF0YS1pc29sYXRpb24tNywgcmVzdWx0cyA9ICdoaWRlJ30KanNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCA9PSAxIHwgZmVlbFJ1c2hlZCA9PSAyIHwgZmVlbFJ1c2hlZCA9PSAzKSB8PiAKICBoZWFkKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KanNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCA9PSAxIHwgZmVlbFJ1c2hlZCA9PSAyIHwgZmVlbFJ1c2hlZCA9PSAzKSB8PiAKICBoZWFkKCkgfD4KICBrYmwoKSB8PgogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+CiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikKYGBgCgpgYGB7ciwgZGF0YS1pc29sYXRpb24tMjAsIHJlc3VsdHMgPSAnaGlkZSd9CmpzX2RhdGEgfD4gCiAgZmlsdGVyKGZlZWxSdXNoZWQgPT0gNCB8IGZlZWxSdXNoZWQgPT0gNSB8IGZlZWxSdXNoZWQgPT0gNikgfD4KICBoZWFkKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KanNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCA9PSA0IHwgZmVlbFJ1c2hlZCA9PSA1IHwgZmVlbFJ1c2hlZCA9PSA2KSB8PgogIGhlYWQoKSB8PgogIGtibCgpIHw+CiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQpgYGAKClRoaXMgbWV0aG9kIHZlcnkgZXhwbGljaXRseSB0ZXN0cyBmb3Igcm93cyB3aGVyZSB0aGUgYGZlZWxSdXNoZWRgIHZhbHVlIGlzIGVpdGhlciBgMWAsIGAyYCwgb3IgYDNgLCBvciBmb3Igcm93cyB3aGVyZSB0aGUgdmFsdWUgaXMgZWl0aGVyIGA0YCwgYDVgIG9yIGA2YC4gCgpBZ2FpbiwgbGV0J3MgY2FsY3VsYXRlIGhvdyBtYW55IHJvd3MgcmVtYWluIGFmdGVyIHdlIHBlcmZvcm1lZCBmaWx0ZXJpbmcuCgpgYGB7cn0KcnVzaGVkX3Jvd3MgPC0ganNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCA9PSAxIHwgZmVlbFJ1c2hlZCA9PSAyIHwgZmVlbFJ1c2hlZCA9PSAzKSB8PgogIG5yb3coKQpub3RfcnVzaGVkX3Jvd3MgPC0ganNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCA9PSA0IHwgZmVlbFJ1c2hlZCA9PSA1IHwgZmVlbFJ1c2hlZCA9PSA2KSB8PgogIG5yb3coKQoKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2Ygcm93cyBpbiBydXNoZWQgaXM6IiwgcnVzaGVkX3Jvd3MpKQpwcmludChwYXN0ZSgiVGhlIG51bWJlciBvZiByb3dzIGluIG5vdCBydXNoZWQgaXM6Iiwgbm90X3J1c2hlZF9yb3dzKSkKYGBgCgpBcyB3ZSBjYW4gc2VlLCBsb29raW5nIGF0IHRoZSByZW1haW4gcm93cyBhZnRlciBmaWx0ZXJpbmcsIGl0IHByb2R1Y2VzIHRoZSBzYW1lIG91dHB1dCBhcyBwcmV2aW91c2x5LgoKVGhlcmUgaXMgYWxzbyBhIHRoaXJkLCBtb3JlIGVsZWdhbnQgYXBwcm9hY2ggdXNpbmcgdGhlIGAlaW4lYCBvcGVyYXRvciwgYnV0IHdlIHdpbGwgZXhwbG9yZSB0aGlzIG1ldGhvZCBpbiB0aGUgZXhlcmNpc2VzIGF0IHRoZSBlbmQgb2YgdGhpcyBzZXNzaW9uLgoKIyMjIDQ6IFNlbGVjdCBSZWxldmFudCBWYXJpYWJsZXMKCiFbXShpbWFnZXMvMy1BQ1QtMy1TZWxlY3QucG5nKQoKIyMjIyBTZWxlY3RpbmcgYW5kIENsZWFuaW5nIFRpbWUtUmVsYXRlZCBWYXJpYWJsZXMKCjwhLS0gIyMjIFNlbGVjdGluZyBhbmQgQ2xlYW5pbmcgVGltZS1SZWxhdGVkIFZhcmlhYmxlcyAtLT4KCk5leHQsIHdlJ2xsIGZvY3VzIG9uIHRoZSBrZXkgdGltZSBhbGxvY2F0aW9uIGNvbHVtbnMgdGhhdCBhcmUgbW9zdCByZWxldmFudCB0byBvdXIgYW5hbHlzaXM6IGBkdXJXb3JrYCwgYGR1clNsZWVwYCwgYW5kIGBkdXJBbG9uZWAuIFRoZXNlIHZhcmlhYmxlcyByZXByZXNlbnQ6CgotICAgYGR1cldvcmtgOiBUaW1lIHNwZW50IG9uIHBhaWQgd29yayBhY3Rpdml0aWVzCi0gICBgZHVyU2xlZXBgOiBUaW1lIHNwZW50IHNsZWVwaW5nLCByZXN0aW5nLCBvciByZWxheGluZwotICAgYGR1ckFsb25lYDogVGltZSBzcGVudCBhbG9uZQoKOjo6IG5vdGUKVGhlIGBzZWxlY3QoKWAgZnVuY3Rpb24gaW4gYGRwbHlyYCBpcyB1c2VkIHRvIGNob29zZSBzcGVjaWZpYyBjb2x1bW5zIGZyb20gYSBkYXRhc2V0Lgo6OjoKCldlJ2xsIHNlbGVjdCBvbmx5IHRoZXNlIHRocmVlIGNvbHVtbnMgZnJvbSBvdXIgcnVzaGVkIGFuZCBub3RfcnVzaGVkIGRhdGFzZXRzIHVzaW5nIGBzZWxlY3QoKWAuCgpgYGB7cn0KcnVzaGVkX3RpbWUgPC0ganNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCA8PSAzKSB8PgogIHNlbGVjdChkdXJXb3JrLCBkdXJTbGVlcCwgZHVyQWxvbmUpIAoKbm90X3J1c2hlZF90aW1lIDwtIGpzX2RhdGEgfD4gCiAgZmlsdGVyKGZlZWxSdXNoZWQgPiAzKSB8PiAKICBzZWxlY3QoZHVyV29yaywgZHVyU2xlZXAsIGR1ckFsb25lKQpgYGAKCjo6OiB3YWxrdGhyb3VnaApJbiB0aGlzIGV4YW1wbGUsIHdlIHVzZSB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbiB0byBleHRyYWN0IG9ubHkgdGhlIHJlbGV2YW50IGNvbHVtbnMgZnJvbSB0aGUgZGF0YXNldCByZXN1bHRpbmcgZnJvbSBmaWx0ZXJpbmcgcm93cyBvZiB0aG9zZSB3aG8gZmVlbCBydXNoZWQgKGkuZS4sIGBmZWVsUnVzaGVkIDw9M2ApIGFuZCB0aG9zZSB3aG8gZG9uJ3QgZmVlbCBydXNoZWQgKGkuZS4sIGBmZWVsUnVzaGVkID4gM2ApLgoKLSAgIGBzZWxlY3QoZHVyV29yaywgZHVyU2xlZXAsIGR1ckFsb25lKWAgdGVsbHMgUiB0byBrZWVwIG9ubHkgdGhlc2UgdGhyZWUgY29sdW1uczoKICAgIC0gICBgZHVyV29ya2A6IGR1cmF0aW9uIG9mIHBhaWQgd29yawogICAgLSAgIGBkdXJTbGVlcGA6IGR1cmF0aW9uIG9mIHNsZWVwIGFuZCByZXN0CiAgICAtICAgYGR1ckFsb25lYDogZHVyYXRpb24gb2YgdGltZSBzcGVudCBhbG9uZQotICAgVGhlIGB8PmAgKHBpcGUgb3BlcmF0b3IpIHBhc3NlcyB0aGUgZGF0YSBmcmFtZXMgb24gdGhlIGxlZnQgaW50byB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbi4KClRoaXMgc3RlcCBoZWxwcyByZWR1Y2UgdGhlIGRhdGFzZXQgdG8gb25seSB0aGUgdmFyaWFibGVzIHdlIGNhcmUgYWJvdXQgZm9yIGNvbXBhcmluZyBob3cgcnVzaGVkIGFuZCBub3QtcnVzaGVkIGluZGl2aWR1YWxzIGFsbG9jYXRlIHRoZWlyIHRpbWUuCjo6OgoKSW4gdGhlIHByZXZpb3VzIGNvZGUsIHdlIGFzc2lnbmVkIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZXMgdG8gdGhlIHZhcmlhYmxlcyBgbm90X3J1c2hlZF90aW1lYCBhbmQgYHJ1c2hlZF90aW1lYC4gVGhlc2UgdmFyaWFibGVzIHdpbGwgYmUgdXNlZnVsIHdoZW4gd2UgY29tcGFyZSB0aW1lIHVzYWdlIGJldHdlZW4gdHdvIGdyb3Vwcy4KCkxldCdzIGV4YW1pbmUgdGhlIGZpcnN0IGZldyByb3dzIG9mIGBydXNoZWRfdGltZWAgYW5kIGBub3RfcnVzaGVkX3RpbWVgLgoKYGBge3IsIGRhdGEtaXNvbGF0aW9uLTkxLCByZXN1bHRzID0gJ2hpZGUnfQpydXNoZWRfdGltZSB8PiAKICBoZWFkKCkKCm5vdF9ydXNoZWRfdGltZSB8PiAKICBoZWFkKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KaGVhZChydXNoZWRfdGltZSkgfD4KICBrYmwoKSB8PgogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+CiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikKCmhlYWQobm90X3J1c2hlZF90aW1lKSB8PgogIGtibCgpIHw+CiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQpgYGAKCkFzIHdlIGNhbiBzZWUsIG91ciBkYXRhIGZyYW1lIHJlc3VsdGluZyBmcm9tIHVzaW5nIGBzZWxlY3QoKWAgb25seSBjb250YWluIDMgY29sdW1uczogYGR1clNsZWVwYCwgYGR1ckFsb25lYCwgYW5kIGBkdXJXb3JrYAoKOjo6IG5vdGUKKipIZWxwZXIgRnVuY3Rpb25zIGZvciBgc2VsZWN0KClgKioKClN1cHBvc2Ugd2Ugd2FudCB0byBwdWxsIG91dCBldmVyeSBjb2x1bW4gaW4gYGpzX2RhdGFgIHdob3NlIG5hbWUgcmVmZXJzIHRvICJkdXJhdGlvbiItLS1mb3IgZXhhbXBsZSwgYGR1cldvcmtgLCBgZHVyRHJpdmluZ2AsIGBkdXJTY2hvb2xTaXRlYCwgYW5kIHNvIG9uLiBXZSBjb3VsZCBtYW51YWxseSBsaXN0IHRoZW0gaW4gYHNlbGVjdCgpYCwgYnV0IHRoYXQgcXVpY2tseSBiZWNvbWVzIHRlZGlvdXMgYW5kIGVycm9yLXByb25lIGFzIG91ciBkYXRhIGdyb3dzLgoKYGRwbHlyYCBwcm92aWRlcyBhIGZhbWlseSBvZiAqKmhlbHBlcioqIGZ1bmN0aW9ucyBmb3IgZXhhY3RseSB0aGlzIGtpbmQgb2YgdGFzay4gSW4gdGhpcyBjYXNlLCBgc3RhcnRzX3dpdGgoImR1ciIpYCB3aWxsIG1hdGNoIGV2ZXJ5IHZhcmlhYmxlIHdob3NlIG5hbWUgYmVnaW5zIHdpdGggImR1ciIuIFdlIGNhbiBwYXNzIHRoYXQgaGVscGVyIGRpcmVjdGx5IHRvIGBzZWxlY3QoKWA6CgpgYGB7ciwgZGF0YS1pc29sYXRpb24tOSwgcmVzdWx0cyA9ICdoaWRlJ30KanNfZGF0YSB8PiAKICBzZWxlY3Qoc3RhcnRzX3dpdGgoImR1ciIpKSB8PgogIGhlYWQoKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpqc19kYXRhIHw+IAogIHNlbGVjdChzdGFydHNfd2l0aCgiZHVyIikpIHw+CiAgaGVhZCgpIHw+CiAga2JsKCkgfD4KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PgogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKVGhlcmUgYXJlIG1hbnkgb3RoZXIgaGVscGVycywgc3VjaCBhcyBgZW5kc193aXRoKClgLCBgY29udGFpbnMoKWAsIGBtYXRjaGVzKClgLCBhbmQgbW9yZSwgd2hpY2ggbGV0IHVzIGJ1aWxkIGNsZWFuLCBmbGV4aWJsZSBjb2RlIHdpdGhvdXQgaGF2aW5nIHRvIHNwZWxsIG91dCBldmVyeSB2YXJpYWJsZSBuYW1lLiBXZSBjYW4gY2hlY2sgdGhlIHNlbGVjdCBbcmVmZXJlbmNlXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3NlbGVjdC5odG1sKSBpbiB0aWR5dmVyc2UgZm9yIG1vcmUgaW5mb3JtYXRpb24uCjo6OgoKIyMjIDU6IENvbXBhcmUgVGltZSBVc2UKCiMjIyMgQ29tcGFyaW5nIFRpbWUgVXNhZ2UgQmV0d2VlbiBHcm91cHMKCjwhLS0gIyMjIFJlc3VsdHM6IENvbXBhcmluZyBUaW1lIFVzYWdlIEJldHdlZW4gR3JvdXBzIC0tPgoKTm93IHRoYXQgd2UgaGF2ZSBvdXIgY2xlYW5lZCBkYXRhc2V0cyBmb3IgYm90aCBydXNoZWQgYW5kIG5vdCBydXNoZWQgZ3JvdXBzLCB3ZSBjYW4gYW5hbHl6ZSBob3cgdGhlc2UgZ3JvdXBzIGRpZmZlciBpbiB0aGVpciB0aW1lIGFsbG9jYXRpb24gcGF0dGVybnMuIFdlJ2xsIGNhbGN1bGF0ZSBhbmQgY29tcGFyZSB0aGUgbWVhbiBkdXJhdGlvbnMgZm9yIHNsZWVwLCB3b3JrLCBhbmQgYWxvbmUgdGltZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzLgoKTGV0J3MgYnJlYWsgdGhpcyBkb3duIGludG8gc3RlcHM6IDEuIEZpcnN0LCB3ZSdsbCBjYWxjdWxhdGUgdGhlIG1lYW4gdmFsdWVzIGZvciBlYWNoIHRpbWUtdXNlIHZhcmlhYmxlIHdpdGhpbiBlYWNoIGdyb3VwLiAyLiBUaGVuLCB3ZSdsbCBjb21wdXRlIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZXNlIG1lYW5zIHRvIHVuZGVyc3RhbmQgdGhlIG1hZ25pdHVkZSBvZiB2YXJpYXRpb24uCgpXZSBjYW4gY3JlYXRlIGEgc3VtbWFyeSB0YWJsZSB0aGF0IGNvbnRhaW5zIHRoZSBtZWFuIHZhbHVlcyBvZiBgZHVyU2xlZXBgLCBgZHVyQWxvbmVgLCBhbmQgYGR1cldvcmtgIGZvciBlYWNoIG9mIHRoZSBydXNoZWRfdGltZSBhbmQgbm90X3J1c2hlZF90aW1lIGRhdGFmcmFtZXM6CgpgYGB7cn0KbWVhbl9ydXNoZWQgPC0gcnVzaGVkX3RpbWUgfD4gCiAgc3VtbWFyaXNlKAogICAgZHVyU2xlZXAgPSBtZWFuKGR1clNsZWVwLCBuYS5ybSA9IFRSVUUpLAogICAgZHVyQWxvbmUgPSBtZWFuKGR1ckFsb25lLCBuYS5ybSA9IFRSVUUpLAogICAgZHVyV29yayA9IG1lYW4oZHVyV29yaywgbmEucm0gPSBUUlVFKQogICkKCm1lYW5fbm90X3J1c2hlZCA8LSBub3RfcnVzaGVkX3RpbWUgfD4gCiAgc3VtbWFyaXNlKAogICAgZHVyU2xlZXAgPSBtZWFuKGR1clNsZWVwLCBuYS5ybSA9IFRSVUUpLAogICAgZHVyQWxvbmUgPSBtZWFuKGR1ckFsb25lLCBuYS5ybSA9IFRSVUUpLAogICAgZHVyV29yayA9IG1lYW4oZHVyV29yaywgbmEucm0gPSBUUlVFKQogICkKYGBgCgo6Ojogd2Fsa3Rocm91Z2gKSG93IGl0IHdvcmtzOgoKLSAgIGBzdW1tYXJpc2UoKWAgY29sbGFwc2VzIGVhY2ggZGF0YSBmcmFtZSBkb3duIHRvIGEgc2luZ2xlIHJvdy4KLSAgIEluc2lkZSwgd2UgZXhwbGljaXRseSBuYW1lIGVhY2ggbmV3IGNvbHVtbiAoYGR1clNsZWVwYCwgYGR1ckFsb25lYCwgYGR1cldvcmtgKSBhbmQgYXNzaWduIGl0IGBtZWFuKG9sZF9jb2x1bW4sIG5hLnJtID0gVFJVRSlgLgotICAgYG5hLnJtID0gVFJVRWAgbWFrZXMgc3VyZSBtaXNzaW5nIHZhbHVlcyBkb24ndCB0aHJvdyBvZmYgb3VyIGF2ZXJhZ2VzLgo6OjoKCkhvd2V2ZXIsIHdlIGNhbiBzZWUgdGhlcmUncyBhIGxpdHRsZSBiaXQgb2YgcmVwZXRpdGlvbiBpbiB0aGUgY29kZSBhYm92ZS4gV2UgY2FuIHdyaXRlIG11Y2ggY2xlYW5lciBjb2RlIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiBmb3IgZXZlcnkgY29sdW1uIGF0IG9uY2UuCgpgYGB7cn0KIyBDYWxjdWxhdGUgdGhlIG1lYW4gdmFsdWVzIGZvciBlYWNoIHZhcmlhYmxlIHVzaW5nIHN1bW1hcmlzZSBhbmQgYWNyb3NzCm1lYW5fcnVzaGVkIDwtIHJ1c2hlZF90aW1lIHw+IAogIHN1bW1hcmlzZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+IG1lYW4oLiAsIG5hLnJtID0gVFJVRSkpKQoKbWVhbl9ub3RfcnVzaGVkIDwtIG5vdF9ydXNoZWRfdGltZSB8PiAKICBzdW1tYXJpc2UoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfiBtZWFuKC4gLCBuYS5ybSA9IFRSVUUpKSkKCmBgYAoKOjo6IHdhbGt0aHJvdWdoCkluIHRoaXMgY29kZToKCi0gICBXZSB1c2UgYHN1bW1hcmlzZSgpYCB3aXRoIGBhY3Jvc3MoKWAgdG8gY2FsY3VsYXRlIG1lYW5zIGZvciBhbGwgY29sdW1ucyBhdCBvbmNlCi0gICBUaGUgYG5hLnJtID0gVFJVRWAgYXJndW1lbnQgZW5zdXJlcyB3ZSBleGNsdWRlIG1pc3NpbmcgdmFsdWVzIGZyb20gb3VyIGNhbGN1bGF0aW9ucwo6OjoKCldlIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBpbiBtZWFucyBiZXR3ZWVuIHRoZXNlIHR3byBncm91cHMuCgpgYGB7cn0KZGlmZl9tZWFucyA8LSBtZWFuX3J1c2hlZCAtIG1lYW5fbm90X3J1c2hlZApgYGAKClRoZSBzdWJ0cmFjdGlvbiAoYG1lYW5fcnVzaGVkIC0gbWVhbl9ub3RfcnVzaGVkYCkgZ2l2ZXMgYSBuZXcgb25l4oCQcm93IHRpYmJsZSBzaG93aW5nIGhvdyBtdWNoIG1vcmUgKG9yIGxlc3MpIHRpbWUgInJ1c2hlZCIgaW5kaXZpZHVhbHMgc3BlbmQgb24gZWFjaCBhY3Rpdml0eSBjb21wYXJlZCB0byAibm90IHJ1c2hlZCIgaW5kaXZpZHVhbHMuCgpOb3csIGxldCdzIHNlZSB3aGF0IHdlIGdldCB1cCB0byB0aGlzIHBvaW50LgoKYGBge3IsIGVjaG89RkFMU0V9CiMgUHJpbnQgdGhlIHJlc3VsdHMKcHJpbnQoIk1lYW4gdmFsdWVzIGZvciByZXNwb25kZW50cyB3aG8gZmVlbCBydXNoZWQ6IikKbWVhbl9ydXNoZWQgfD4KICBrYmwoKSB8PgogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+CiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikKCnByaW50KCJNZWFuIHZhbHVlcyBmb3IgcmVzcG9uZGVudHMgd2hvIGRvIG5vdCBmZWVsIHJ1c2hlZDoiKQptZWFuX25vdF9ydXNoZWQgfD4KICBrYmwoKSB8PgogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+CiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikKCnByaW50KCJEaWZmZXJlbmNlIGJldHdlZW4gcnVzaGVkIGFuZCBub3QgcnVzaGVkIChydXNoZWQgLSBub3QgcnVzaGVkKToiKQpkaWZmX21lYW5zIHw+CiAga2JsKCkgfD4KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PgogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKOjo6IG5vdGUKUG9zaXRpdmUgdmFsdWVzIGluIHRoZSBkaWZmZXJlbmNlIGNhbGN1bGF0aW9uIGluZGljYXRlIHRoYXQgcnVzaGVkIGluZGl2aWR1YWxzIHNwZW5kIG1vcmUgdGltZSBvbiB0aGF0IGFjdGl2aXR5LCB3aGlsZSBuZWdhdGl2ZSB2YWx1ZXMgaW5kaWNhdGUgdGhleSBzcGVuZCBsZXNzIHRpbWUgY29tcGFyZWQgdG8gdGhvc2Ugd2hvIGRvbid0IGZlZWwgcnVzaGVkLgo6OjoKClRoZXNlIHJlc3VsdHMgcHJvdmlkZSBpbnRlcmVzdGluZyBpbnNpZ2h0cyBpbnRvIGhvdyBmZWVsaW5nIHJ1c2hlZCByZWxhdGVzIHRvIHRpbWUgYWxsb2NhdGlvbiBwYXR0ZXJucy4gRm9yIGluc3RhbmNlLCB3ZSBjYW4gb2JzZXJ2ZSB3aGV0aGVyIHBlb3BsZSB3aG8gZmVlbCBydXNoZWQgYWN0dWFsbHkgc3BlbmQgbW9yZSB0aW1lIHdvcmtpbmcgb3IgbGVzcyB0aW1lIHNsZWVwaW5nIHRoYW4gdGhvc2Ugd2hvIGRvbid0IGZlZWwgcnVzaGVkLCB3aGljaCBtaWdodCBoZWxwIGV4cGxhaW4gdGhlaXIgcGVyY2VwdGlvbiBvZiB0aW1lIHByZXNzdXJlLgoKIyMgUmVjb2RlICYgU2F2ZQoKSW4gdGhlIG5leHQgZXhlcmNpc2UsIHdlIHdpbGwgY29tcGFyZSB0aGUgdGltZSBwcmVzc3VyZSBmZWx0IGJ5IHJlc3BvbmRlbnRzIGJhc2VkIG9uIHdoZXRoZXIgdGhleSBsaXZlIGluIGFuICoqdXJiYW4qKiBvciAqKnJ1cmFsKiogYXJlYSwgYXMgY29kZWQgaW4gdGhlIGBwb3BDZW50ZXJgIGNvbHVtbjoKCmBgYHtyfQpqc19kYXRhIHw+CiAgY291bnQocG9wQ2VudGVyKQpgYGAKCkFjY29yZGluZyB0byB0aGUgZGF0YSBkaWN0aW9uYXJ5LCB0aGVzZSBudW1lcmljIGNvZGVzIG1lYW46Cgo6OjptZC10YWJsZQp8IHZhbHVlIHwgbGFiZWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IDEgICAgIHwgTGFyZ2VyIHVyYmFuIHBvcHVsYXRpb24gY2VudHJlcyAoQ01BL0NBKSAgICAgICAgICAgICAgfAp8IDIgICAgIHwgUnVyYWwgYXJlYXMgYW5kIHNtYWxsIHBvcHVsYXRpb24gY2VudHJlcyAobm9uIENNQS9DQSkgfAp8IDMgICAgIHwgUHJpbmNlIEVkd2FyZCBJc2xhbmQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAo6OjoKCldlIGNhbiB1c2UgKipkcGx5cioqJ3MgYG11dGF0ZSgpYCBhbG9uZyB3aXRoIGBpZl9lbHNlKClgIHRvIHR1cm4gdGhvc2UgbnVtYmVycyBpbnRvIGRlc2NyaXB0aXZlIGxhYmVsczoKCmBgYHtyfQpqc19kYXRhIDwtIGpzX2RhdGEgfD4gbXV0YXRlKAogIHBvcENlbnRlcj1pZl9lbHNlKHBvcENlbnRlcj09MSwndXJiYW4nLAogICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UocG9wQ2VudGVyPT0yLCdydXJhbCcsJ1BFSScpKQopCmBgYAoKOjo6IHdhbGt0aHJvdWdoCkhvdyBpdCB3b3JrczoKCi0gICBgbXV0YXRlKClgOiBhZGRzIG9yIG1vZGlmaWVzIGNvbHVtbnMuCi0gICBgaWZfZWxzZShjb25kaXRpb24sIHRydWUsIGZhbHNlKWA6IGEgdmVjdG9yaXplZCwgdHlwZS1zYWZlIGNvbmRpdGlvbmFsLgotICAgSGVyZSwgd2UgbmVzdCB0d28gYGlmX2Vsc2UoKWAgY2FsbHMgc28gdGhhdDoKICAgIC0gICBgcG9wQ2VudGVyID09IDFgIOKGkiAidXJiYW4iCiAgICAtICAgYHBvcENlbnRlciA9PSAyYCDihpIgInJ1cmFsIgogICAgLSAgIG90aGVyd2lzZSAoY29kZSBgM2ApIOKGkiAiUEVJIgo6OjoKCk5leHQsIHdlJ2xsIGNyZWF0ZSBhIG5ldyBmbGFnIGNhbGxlZCBgaXNGZWVsUnVzaGVkYCB0byBtYXJrIHdoZXRoZXIgZWFjaCByZXNwb25kZW50IGZlZWxzIHJ1c2hlZCBvciBub3QuIFRoaXMgd2lsbCBiZSB1c2VmdWwgZm9yIG91ciB1cGNvbWluZyBkYXRhLXZpc3VhbGl6YXRpb24gdHV0b3JpYWw6CgpgYGB7cn0KanNfZGF0YSA8LSBqc19kYXRhIHw+IG11dGF0ZSgKICBpc0ZlZWxSdXNoZWQ9aWZfZWxzZShmZWVsUnVzaGVkIDw9IDMsMSwwKQopCmBgYAoKOjo6IHdhbGt0aHJvdWdoCkluIHRoaXMgY29kZToKCi0gICBXZSBhc3NpZ24gdGhlIHJlc3VsdCBiYWNrIGludG8gYGpzX2RhdGFgIHNvIHRoZSBuZXcgY29sdW1uIGlzIHNhdmVkLgotICAgVGhlIGBpZl9lbHNlKClgIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgYmluYXJ5IHZhcmlhYmxlOgogICAgLSAgIGBpc0ZlZWxSdXNoZWQgPSAxYCBpZiBgZmVlbFJ1c2hlZGAgaXMgYDFgLCBgMmAsIG9yIGAzYCAoaS5lLiB0aGUgcmVzcG9uZGVudCBmZWVscyBydXNoZWQpCiAgICAtICAgYGlzRmVlbFJ1c2hlZCA9IDBgIG90aGVyd2lzZSAoaS5lLiBub3QgcnVzaGVkKQo6OjoKCkZpbmFsbHksIHNhdmUgdGhlIHJlY29kZWQgZGF0YXNldCBzbyBpdCdzIHJlYWR5IGZvciB0aGUgbmV4dCBzdGVwczoKCmBgYHtyfQpzYXZlKGpzX2RhdGEsIGZpbGUgPSAiZGF0YS90aW1lX3VzZV9kYXkzXzIuUkRhdGEiKQpgYGAKCiMjIEV4ZXJjaXNlczogVGltZSBmb3IgUHJhY3RpY2UhCgojIyMgVXNpbmcgYCVpbiVgIE9wZXJhdG9yCgpJbiB0aGUgcHJldmlvdXMgc2VjdGlvbnMsIHdlIGxlYXJuZWQgdGhhdCB3ZSBjYW4gZmlsdGVyIHJlc3BvbmRlbnRzIHdobyBmZWVsIHJ1c2hlZCBvciBub3QgYnkgZWl0aGVyIHVzaW5nIGEgcmFuZ2UgY29tcGFyaXNvbiAoZS5nLiwgYGZlZWxSdXNoZWQgPD0gM2ApIG9yIGJ5IGNoYWluaW5nIG11bHRpcGxlIGNvbmRpdGlvbnMgd2l0aCBsb2dpY2FsIG9wZXJhdG9ycyAoZS5nLiwgYGZlZWxSdXNoZWQgPT0gMSB8IGZlZWxSdXNoZWQgPT0gMiB8IGZlZWxSdXNoZWQgPT0gM2ApLgoKSG93ZXZlciwgdGhlcmUncyBhbHNvIGEgdGhpcmQsIG1vcmUgZWxlZ2FudCBhcHByb2FjaDogdXNpbmcgdGhlIGAlaW4lYCBvcGVyYXRvci4gVGhpcyBtZXRob2QgaXMgZXNwZWNpYWxseSBoZWxwZnVsIHdoZW4gd2Ugd2FudCB0byBmaWx0ZXIgYSBkYXRhc2V0IGJhc2VkIG9uIG11bHRpcGxlIHNwZWNpZmljIHZhbHVlcyBvZiBhIHZhcmlhYmxlLCBtYWtpbmcgb3VyIGNvZGUgc2hvcnRlciBhbmQgZWFzaWVyIHRvIHJlYWQuCgo6OjogcXVlc3Rpb24KKipFeGFtcGxlIFNjZW5hcmlvKioKClJlbWVtYmVyIHRoZSBwcmV2aW91cyBxdWVzdGlvbiB3aGVyZSB3ZSdyZSBhbmFseXppbmcgc3VydmV5IGRhdGEgb24gaG93IGZyZXF1ZW50bHkgcGVvcGxlIGZlZWwgcnVzaGVkLiBUaGUgYGZlZWxSdXNoZWRgIHZhcmlhYmxlIGluY2x1ZGVzIHZhbHVlcyBmcm9tIDEgKGRhaWx5KSB0byA2IChuZXZlcikuIFdlIHdhbnQgdG8gZ3JvdXAgcmVzcG9uZGVudHMgaW50byB0d28gY2F0ZWdvcmllczoKCi0gICAqKlJ1c2hlZCoqOiB0aG9zZSB3aG8gZmVlbCBydXNoZWQgYXQgbGVhc3Qgb25jZSBhIHdlZWsgKGAxYCwgYDJgLCBgM2ApCi0gICAqKk5vdCBSdXNoZWQqKjogdGhvc2Ugd2hvIGZlZWwgcnVzaGVkIGxlc3Mgb2Z0ZW4gKGA0YCwgYDVgLCBgNmApCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCioqVGFzayBJbnN0cnVjdGlvbnMqKgoKMS4gIERlZmluZSB0aGUgc2V0cyBvZiB2YWx1ZXMgdGhhdCByZXByZXNlbnQgZWFjaCBncm91cC4KMi4gIFVzZSB0aGUgYCVpbiVgIG9wZXJhdG9yIGluc2lkZSBgZmlsdGVyKClgIHRvIGNyZWF0ZSBzdWJzZXRzLgozLiAgRGlzcGxheSB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgZWFjaCBzdWJzZXQgdG8gdmVyaWZ5IGNvcnJlY3RuZXNzLgo0LiAgQ291bnQgdGhlIG51bWJlciBvZiByb3dzIGluIGVhY2ggZ3JvdXAgdG8gY29tcGFyZSBzaXplcy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKKipTdGVwcyAxICYgMjogRGVmaW5lIHRoZSBzZXRzIGFuZCB1c2UgYCVpbiVgIGluc2lkZSBgZmlsdGVyKClgKioKCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCByZXN1bHRzID0gJ2hpZGUnfQojIERlZmluZSBncm91cCBsZXZlbHMKcnVzaGVkX2xldmVscyA8LSBjKDEsIDIsIDMpCm5vdF9ydXNoZWRfbGV2ZWxzIDwtIGMoNCwgNSwgNikKCiMgRmlsdGVyIHVzaW5nICVpbiUKanNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCAlaW4lIHJ1c2hlZF9sZXZlbHMpCgpqc19kYXRhIHw+IAogIGZpbHRlcihmZWVsUnVzaGVkICVpbiUgbm90X3J1c2hlZF9sZXZlbHMpCmBgYAoKKipTdGVwIDNhOiBWaWV3IHRoZSBmaXJzdCBmZXcgcm93cyBvZiBgcnVzaGVkX2xldmVsc2AqKgoKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZScsIHJlc3VsdHMgPSAnaGlkZSd9CmpzX2RhdGEgfD4gCiAgZmlsdGVyKGZlZWxSdXNoZWQgJWluJSBydXNoZWRfbGV2ZWxzKSB8PiAKICBoZWFkKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KanNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCAlaW4lIHJ1c2hlZF9sZXZlbHMpIHw+IAogIGhlYWQoKSB8PiAKICBrYmwoKSB8PiAKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PiAKICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQpgYGAKCioqU3RlcCAzYjogVmlldyB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgYG5vdF9ydXNoZWRfbGV2ZWxzYCoqCgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30KanNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCAlaW4lIG5vdF9ydXNoZWRfbGV2ZWxzKSB8PiAKICBoZWFkKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KanNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCAlaW4lIG5vdF9ydXNoZWRfbGV2ZWxzKSB8PgogIGhlYWQoKSB8PgogIGtibCgpIHw+IAogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+IAogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKKipTdGVwIDQ6IENvdW50IHJvd3MqKgoKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZSd9CgpydXNoZWRfcm93cyA8LSBqc19kYXRhIHw+IAogIGZpbHRlcihmZWVsUnVzaGVkICVpbiUgcnVzaGVkX2xldmVscykgfD4gCiAgbnJvdygpCgpub3RfcnVzaGVkX3Jvd3MgPC0ganNfZGF0YSB8PiAKICBmaWx0ZXIoZmVlbFJ1c2hlZCAlaW4lIG5vdF9ydXNoZWRfbGV2ZWxzKSB8PiAKICBucm93KCkKCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHJvd3MgaW4gcnVzaGVkIGlzOiIsIHJ1c2hlZF9yb3dzKSkKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2Ygcm93cyBpbiBub3QgcnVzaGVkIGlzOiIsIG5vdF9ydXNoZWRfcm93cykpCmBgYAoKKipTdW1tYXJ5KioKClRoaXMgZXhhbXBsZSBzaG93cyBob3cgdGhlIGAlaW4lYCBvcGVyYXRvciBzaW1wbGlmaWVzIGZpbHRlcmluZyB3aGVuIHdvcmtpbmcgd2l0aCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIEl0IGF2b2lkcyB0aGUgbmVlZCB0byB3cml0ZSBtdWx0aXBsZSBgPT1gIGFuZCBgfGAgY29uZGl0aW9ucywgbWFraW5nIG91ciBjb2RlIGNsZWFuZXIgYW5kIGVhc2llciB0byByZWFkLgo6OjoKCiMjIyBUaW1lIFByZXNzdXJlIEFuYWx5c2lzCgo6Ojogd2Fsa3Rocm91Z2gKTGV0J3MgdXNlIG91ciBuZXcgZmlsdGVyaW5nIGFuZCBzZWxlY3Rpbmcgc2tpbGxzIHRvIGV4cGxvcmUgaG93IHBlb3BsZSBpbiB1cmJhbiBhbmQgcnVyYWwgYXJlYXMgZGlmZmVyIGluIHRlcm1zIG9mIHRpbWUgcHJlc3N1cmUsIGJhc2VkIG9uIHRoZWlyIHJlcG9ydGVkIGV4dHJhIHRpbWUuCgoqKlRhc2sgSW5zdHJ1Y3Rpb25zKioKCjEuICBFeHBsb3JlIHRoZSBgcG9wQ2VudGVyYCBhbmQgYGV4dHJhVGltZWAgY29sdW1ucyB0byB1bmRlcnN0YW5kIHRoZSB2YXJpYWJsZSB0eXBlcyBhbmQgcG9zc2libGUgdmFsdWVzLgoyLiAgRmlsdGVyIHRoZSBkYXRhc2V0IGludG8gdHdvIGdyb3VwczogYHVyYmFuYCBhbmQgYHJ1cmFsYC4KMy4gIFNlbGVjdCB0aGUgcmVsZXZhbnQgdmFyaWFibGVzOiBgcG9wQ2VudGVyYCwgYGV4dHJhVGltZWAsIGBkdXJXb3JrYCwgYW5kIGBkdXJTbGVlcGAuCjQuICBDYWxjdWxhdGUgYW5kIGNvbXBhcmUgdGhlIGF2ZXJhZ2UgYGV4dHJhVGltZWAgYmV0d2VlbiB0aGUgdHdvIGdyb3Vwcy4KCioqU3RlcCAxOiBFeHBsb3JlIHRoZSBEYXRhKioKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30KanNfZGF0YSB8PiAKICBkaXN0aW5jdChwb3BDZW50ZXIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmpzX2RhdGEgfD4gCiAgZGlzdGluY3QocG9wQ2VudGVyKSB8PgogIGtibCgpIHw+IAogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+IAogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCByZXN1bHRzID0gJ2hpZGUnfQpqc19kYXRhIHw+IAogIGNvdW50KGV4dHJhVGltZSkgfD4gCiAgYXJyYW5nZShkZXNjKG4pKSB8PiAKICBoZWFkKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KanNfZGF0YSB8PiAKICBjb3VudChleHRyYVRpbWUpIHw+IAogIGFycmFuZ2UoZGVzYyhuKSkgfD4gCiAgaGVhZCgpIHw+CiAga2JsKCkgfD4gCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4gCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikKYGBgCgoqKlN0ZXAgMjogRmlsdGVyIFVyYmFuIGFuZCBSdXJhbCBQb3B1bGF0aW9ucyoqCgpUbyBmaWx0ZXIgdGhlIHVyYmFuIHBvcHVsYXRpb24sIHdlIHVzZSB0aGUgYGZpbHRlcigpYCBmdW5jdGlvbiB0byBrZWVwIG9ubHkgcm93cyB3aGVyZSBgcG9wQ2VudGVyYCBpcyBlcXVhbCB0byBgInVyYmFuImAuIFRoZW4sIHdlIHVzZSBgaGVhZCgpYCB0byBkaXNwbGF5IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgcmVzdWx0aW5nIGRhdGFmcmFtZToKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30KanNfZGF0YSB8PiAKICBmaWx0ZXIocG9wQ2VudGVyID09ICd1cmJhbicpIHw+CiAgaGVhZCgpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmpzX2RhdGEgfD4gCiAgZmlsdGVyKHBvcENlbnRlciA9PSAndXJiYW4nKSB8PgogIGhlYWQoKSB8PiAKICBrYmwoKSB8PiAKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PiAKICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQpgYGAKClNpbWlsYXJseSwgd2UgZmlsdGVyIHRoZSBydXJhbCBwb3B1bGF0aW9uLCB0aGVuIHByZXZpZXcgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSByZXN1bHRpbmcgZGF0YWZyYW1lOgoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCByZXN1bHRzID0gJ2hpZGUnfQpqc19kYXRhIHw+IAogIGZpbHRlcihwb3BDZW50ZXIgPT0gJ3J1cmFsJykgfD4KICBoZWFkKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KanNfZGF0YSB8PiAKICBmaWx0ZXIocG9wQ2VudGVyID09ICdydXJhbCcpIHw+CiAgaGVhZCgpIHw+CiAga2JsKCkgfD4gCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4gCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikKYGBgCgoqKlN0ZXAgMzogU2VsZWN0IFJlbGV2YW50IFZhcmlhYmxlcyoqCgpXZSBzZWxlY3QgdGhlIGZvbGxvd2luZyB2YXJpYWJsZXM6CgotICAgYHBvcENlbnRlcmA6IHRvIGluZGljYXRlIGdyb3VwIGlkZW50aXR5ICh1cmJhbiB2cy4gcnVyYWwpCi0gICBgZXh0cmFUaW1lYDogdG8gbWVhc3VyZSBwZXJjZWl2ZWQgdGltZSBhdmFpbGFiaWxpdHkKLSAgIGBkdXJXb3JrYDogdG8gZXhhbWluZSB3b3JrIGR1cmF0aW9uCi0gICBgZHVyU2xlZXBgOiB0byBldmFsdWF0ZSBzbGVlcCBwYXR0ZXJucwoKV2Ugc3RhcnQgYnkgc2VsZWN0aW5nIHRoZSByZWxldmFudCBjb2x1bW5zIGZyb20gdGhlIGZpbHRlcmVkIHVyYmFuIGRhdGFmcmFtZToKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30KdXJiYW5fc2VsZWN0ZWQgPC0ganNfZGF0YSB8PiAKICBmaWx0ZXIocG9wQ2VudGVyID09ICd1cmJhbicpIHw+IAogIHNlbGVjdChwb3BDZW50ZXIsIGV4dHJhVGltZSwgZHVyV29yaywgZHVyU2xlZXApCgp1cmJhbl9zZWxlY3RlZCB8PiAKICBoZWFkKCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KaGVhZCh1cmJhbl9zZWxlY3RlZCkgfD4KICBrYmwoKSB8PiAKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PiAKICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQpgYGAKClNpbWlsYXJseSwgd2Ugc2VsZWN0IGNvbHVtbnMgZnJvbSB0aGUgZmlsdGVyZWQgcnVyYWwgZGF0YWZyYW1lOgoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCByZXN1bHRzID0gJ2hpZGUnfQpydXJhbF9zZWxlY3RlZCA8LSBqc19kYXRhIHw+IAogIGZpbHRlcihwb3BDZW50ZXIgPT0gJ3J1cmFsJykgfD4gCiAgc2VsZWN0KHBvcENlbnRlciwgZXh0cmFUaW1lLCBkdXJXb3JrLCBkdXJTbGVlcCkgCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmhlYWQocnVyYWxfc2VsZWN0ZWQpIHw+IAogIGtibCgpIHw+IAogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+IAogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpCmBgYAoKKipTdGVwIDQ6IENhbGN1bGF0ZSBhbmQgQ29tcGFyZSBNZWFuIEV4dHJhIFRpbWUqKgoKVG8gY2FsY3VsYXRlIHRoZSBhdmVyYWdlIGFtb3VudCBvZiBleHRyYSB0aW1lIHJlcG9ydGVkIGJ5IHVyYmFuIGFuZCBydXJhbCByZXNwb25kZW50cywgd2UgdXNlIHRoZSBgc3VtbWFyaXNlKClgIGZ1bmN0aW9uOgoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCByZXN1bHRzID0gJ2hpZGUnfQptZWFuX3VyYmFuIDwtIHVyYmFuX3NlbGVjdGVkIHw+IAogIHN1bW1hcmlzZShhdmdfZXh0cmFfdGltZSA9IG1lYW4oZXh0cmFUaW1lLCBuYS5ybSA9IFRSVUUpKQptZWFuX3J1cmFsIDwtIHJ1cmFsX3NlbGVjdGVkIHw+IAogIHN1bW1hcmlzZShhdmdfZXh0cmFfdGltZSA9IG1lYW4oZXh0cmFUaW1lLCBuYS5ybSA9IFRSVUUpKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpwcmludCgiTWVhbiBleHRyYSB0aW1lIGZvciBVcmJhbiByZXNwb25kZW50czoiKQptZWFuX3VyYmFuIHw+IGtibCgpIHw+IGthYmxlX3N0eWxpbmcoKQoKcHJpbnQoIk1lYW4gZXh0cmEgdGltZSBmb3IgUnVyYWwgcmVzcG9uZGVudHM6IikKbWVhbl9ydXJhbCB8PiBrYmwoKSB8PiBrYWJsZV9zdHlsaW5nKCkKYGBgCgpJdCBzZWVtcyB0aGF0LCBvbiBhdmVyYWdlLCByZXNwb25kZW50cyByZXNpZGluZyBpbiBydXJhbCBhcmVhcyBoYXZlIG1vcmUgYGV4dHJhVGltZWAuIEhpZ2hlciB2YWx1ZXMgaW4gYGV4dHJhVGltZWAgaW5kaWNhdGUgZ3JlYXRlciBhdmFpbGFiaWxpdHkgb2YgZnJlZSB0aW1lLgo6OjoKCiMjIFRha2Vhd2F5CgpJbiB0aGlzIHNlc3Npb24sIHdlIGxlYXJuZWQgaG93IHRvIGZpbHRlciByb3dzIGFuZCBzZWxlY3Qgc3BlY2lmaWMgY29sdW1ucyB0byBjcmVhdGUgYSBzbWFsbGVyLCBjbGVhbmVyIGRhdGFzZXQgdGhhdCBoZWxwcyB1cyBhbnN3ZXIgb3VyIGd1aWRpbmcgcXVlc3Rpb24gbW9yZSBlZmZpY2llbnRseS4gVXNpbmcgZnVuY3Rpb25zIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSBsaWtlIGBmaWx0ZXIoKWAsIGBzZWxlY3QoKWAsIGFuZCBgbXV0YXRlKClgLCB3ZSBwcmFjdGljZWQgaG93IHRvOgoKLSAgIEZpbHRlciByb3dzIGJhc2VkIG9uIGNvbmRpdGlvbnMgdXNpbmcgY29tcGFyaXNvbiBhbmQgbG9naWNhbCBvcGVyYXRvcnNcCi0gICBTZWxlY3Qgb25seSB0aGUgdmFyaWFibGVzIHdlIGNhcmUgYWJvdXQgdXNpbmcgYHNlbGVjdCgpYCBhbmQgaGVscGVyIGZ1bmN0aW9ucyBsaWtlIGBzdGFydHNfd2l0aCgpYFwKLSAgIFVzZSBgc3VtbWFyaXNlKClgIGFuZCBgYWNyb3NzKClgIHRvIGNhbGN1bGF0ZSBtZWFuIHZhbHVlcyBmb3IgZW50aXJlIGdyb3Vwc1wKLSAgIFJlY29kZSB2YXJpYWJsZXMgbGlrZSBgcG9wQ2VudGVyYCBhbmQgY3JlYXRlIG5ldyBmbGFncyBsaWtlIGBpc0ZlZWxSdXNoZWRgIHdpdGggYG11dGF0ZSgpYAoKVGhlc2UgdGVjaG5pcXVlcyBoZWxwIHVzIHpvb20gaW4gb24gdGhlIHBhcnRzIG9mIHRoZSBkYXRhIHRoYXQgbWF0dGVyIG1vc3QgYW5kIHByZXBhcmUgZm9yIGRlZXBlciBleHBsb3JhdGlvbiBpbiB0aGUgbmV4dCBzZXNzaW9ucy4KCg==