Data Filtering and Selection Techniques

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:
- What subsetting means and why it is crucial
- 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 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.
load("data/timeuse_clean-cols_recoded.RData")
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.
Step 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.
== |
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.
& |
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.
| 1 |
daily |
| 2 |
few Times a Week |
| 3 |
once a Week |
| 4 |
once a Month |
| 5 |
less a Month |
| 6 |
never |
Step 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.
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.
Step 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.
Step 4: Select Relevant Variables

Selecting and Cleaning Time-Related Variables
Next, we’ll focus on the key time allocation columns that are most
relevant to our analysis: durWork, durSleep,
and durAlone. These variables represent:
durWork: Time spent on paid work activities
durSleep: Time spent sleeping, resting, or
relaxing
durAlone: Time spent alone
The select() function in dplyr is used to
choose specific columns from a dataset.
We’ll select only these three columns from our rushed and not_rushed
datasets using select().
rushed_time <- js_data |>
filter(feelRushed <= 3) |>
select(durWork, durSleep, durAlone)
not_rushed_time <- js_data |>
filter(feelRushed > 3) |>
select(durWork, durSleep, durAlone)
In this example, we use the select() function to extract
only the relevant columns from the dataset resulting from filtering rows
of those who feel rushed (i.e., feelRushed <=3) and
those who don’t feel rushed (i.e., feelRushed > 3).
select(durWork, durSleep, durAlone) tells R to keep
only these three columns:
durWork: duration of paid work
durSleep: duration of sleep and rest
durAlone: duration of time spent alone
- The
|> (pipe operator) passes the data frames on the
left into the select() function.
This step helps reduce the dataset to only the variables we care
about for comparing how rushed and not-rushed individuals allocate their
time.
In the previous code, we assigned the resulting data frames to the
variables not_rushed_time and rushed_time.
These variables will be useful when we compare time usage between two
groups.
Let’s examine the first few rows of rushed_time and
not_rushed_time.
rushed_time |>
head()
not_rushed_time |>
head()
|
durWork
|
durSleep
|
durAlone
|
|
0
|
510
|
770
|
|
0
|
420
|
0
|
|
480
|
570
|
630
|
|
20
|
510
|
875
|
|
0
|
525
|
815
|
|
530
|
435
|
430
|
|
durWork
|
durSleep
|
durAlone
|
|
0
|
635
|
650
|
|
0
|
440
|
1200
|
|
0
|
540
|
1060
|
|
0
|
510
|
200
|
|
0
|
660
|
1440
|
|
0
|
660
|
640
|
As we can see, our data frame resulting from using
select() only contain 3 columns: durSleep,
durAlone, and durWork
Helper Functions for select()
Suppose we want to pull out every column in js_data
whose name refers to “duration”—for example, durWork,
durDriving, durSchoolSite, and so on. We could
manually list them in select(), but that quickly becomes
tedious and error-prone as our data grows.
dplyr provides a family of helper
functions for exactly this kind of task. In this case,
starts_with("dur") will match every variable whose name
begins with “dur”. We can pass that helper directly to
select():
js_data |>
select(starts_with("dur")) |>
head()
|
durSleep
|
durMealPrep
|
durEating
|
durAlone
|
durDriving
|
durWork
|
durShoolSite
|
durSchoolOnline
|
durStudy
|
|
510
|
60
|
120
|
770
|
90
|
0
|
0
|
0
|
0
|
|
420
|
150
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
|
570
|
0
|
0
|
630
|
30
|
480
|
0
|
0
|
0
|
|
510
|
10
|
45
|
875
|
80
|
20
|
0
|
0
|
0
|
|
525
|
90
|
40
|
815
|
0
|
0
|
0
|
0
|
0
|
|
435
|
0
|
0
|
430
|
40
|
530
|
0
|
0
|
0
|
There are many other helpers, such as ends_with(),
contains(), matches(), and more, which let us
build clean, flexible code without having to spell out every variable
name. We can check the select reference
in tidyverse for more information.
Step 5: Compare Time Use Between Groups
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.
Before the Next Exercise: Recode Variables and Save Dataset
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:
| 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!
Optional Exercise 1: 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
- Define the sets of values that represent each group.
- Use the
%in% operator inside filter() to
create subsets.
- Display the first few rows of each subset to verify
correctness.
- 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.
Optional Exercise 2: Urban vs Rural 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
- Explore the
popCenter and extraTime
columns to understand the variable types and possible values.
- Filter the dataset into two groups:
urban and
rural.
- Select the relevant variables:
popCenter,
extraTime, durWork, and
durSleep.
- 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:"
## [1] "Mean extra time for Rural respondents:"
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.
LS0tDQp0aXRsZTogIkRheSAzIC0gUGFydCAyIg0KcGFnZXRpdGxlOiAiRGF0YSBGaWx0ZXJpbmcgYW5kIFNlbGVjdGlvbiBUZWNobmlxdWVzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBpbmNsdWRlczoNCiAgICAgIGFmdGVyX2JvZHk6IGZvb3Rlci5odG1sDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCi0tLQ0KDQojIyBEYXRhIEZpbHRlcmluZyBhbmQgU2VsZWN0aW9uIFRlY2huaXF1ZXMNCg0KIVtdKGltYWdlcy8zLUFDVC0zLU92ZXJ2aWV3LnBuZykNCg0KIyMgSW50cm9kdWN0aW9uDQoNCjxoNCBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyI+DQoNCjxzdHJvbmc+U3Vic2V0dGluZyBkYXRhIGlzIGFuIGVzc2VudGlhbCBwYXJ0IG9mIHRoZSA8ZW0+dHJhbnNmb3JtPC9lbT4gc3RhZ2UgaW4gdGhlIGRhdGEgc2NpZW5jZSB3b3JrZmxvdy4gSXQgaW52b2x2ZXMgZXh0cmFjdGluZyBhIHBvcnRpb24gb2YgdGhlIGRhdGFzZXQgYmFzZWQgb24gc3BlY2lmaWMgY29uZGl0aW9ucy48L3N0cm9uZz4NCg0KPC9oND4NCg0KSW4gdGhpcyBzaG9ydCB0dXRvcmlhbCwgd2Ugd2lsbCBsZWFybjoNCg0KMS4gIFdoYXQgc3Vic2V0dGluZyBtZWFucyBhbmQgd2h5IGl0IGlzIGNydWNpYWwNCjIuICBIb3cgdG8gcGVyZm9ybSBzdWJzZXR0aW5nIHVzaW5nIGNvbmRpdGlvbmFsIG9wZXJhdG9ycyBhbmQgdGhlIGRwbHlyIGZ1bmN0aW9ucyBgZmlsdGVyKClgIGFuZCBgc2VsZWN0KClgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQo8aDQgc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPg0KDQpXaHkgaXMgc3Vic2V0dGluZyBhIGRhdGFzZXQgaW1wb3J0YW50Pw0KDQo8L2g0Pg0KDQo6Ojogbm90ZQ0KTW9zdCByZWFsLXdvcmxkIGRhdGFzZXRzIGFyZSBsYXJnZSBhbmQgY29tcGxleCwgd2l0aCBtYW55IHZhcmlhYmxlcyBhbmQgdGhvdXNhbmRzIG9mIHJlY29yZHMuDQo6OjoNCg0KTW9zdCByZWFsLXdvcmxkIGRhdGFzZXRzIGFyZSBsYXJnZSBhbmQgY29tcGxleCwgd2l0aCBtYW55IHZhcmlhYmxlcyBhbmQgdGhvdXNhbmRzIG9mIHJlY29yZHMuIEFuYWx5emluZyBhbGwgb2YgaXQgYXQgb25jZSBjYW4gaW50cm9kdWNlIG5vaXNlLCBzbG93IGRvd24gY29tcHV0YXRpb24sIGFuZCBtYWtlIGl0IGhhcmRlciB0byBkZXRlY3QgbWVhbmluZ2Z1bCBwYXR0ZXJucy4gQnkgZmlsdGVyaW5nIHJvd3MgYW5kIHNlbGVjdGluZyBzcGVjaWZpYyBjb2x1bW5zLCB3ZSByZWR1Y2UgdGhpcyBjb21wbGV4aXR5LCBpbXByb3ZlIGNsYXJpdHksIGFuZCBjYW4gbW9yZSBlZmZpY2llbnRseSB0ZXN0IGh5cG90aGVzZXMgb3IgYnVpbGQgbW9kZWxzLiBTdWJzZXR0aW5nIGFsc28gcGxheXMgYSBrZXkgcm9sZSBpbiBlbnN1cmluZyBkYXRhIHF1YWxpdHksIGFsbG93aW5nIHVzIHRvIHJlbW92ZSBpcnJlbGV2YW50IG9yIHByb2JsZW1hdGljIGVudHJpZXMgbGlrZSBtaXNzaW5nIHZhbHVlcywgb3V0bGllcnMsIG9yIGNhdGVnb3JpZXMgb3V0c2lkZSB0aGUgc2NvcGUgb2Ygb3VyIGFuYWx5c2lzLg0KDQo6Ojogbm90ZQ0KRmlsdGVyaW5nIHJvd3MgYW5kIHNlbGVjdGluZyBzcGVjaWZpYyBjb2x1bW5zIGhlbHBzIHVzOg0KDQotICAgUmVkdWNlIGRhdGFzZXQgY29tcGxleGl0eSBhbmQgaW1wcm92ZSBjbGFyaXR5LCBtYWtpbmcgaXQgZWFzaWVyIHRvIHRlc3QgaHlwb3RoZXNlcyBlZmZpY2llbnRseQ0KLSAgIFJlbW92ZSBpcnJlbGV2YW50IG9yIHByb2JsZW1hdGljIGVudHJpZXMgc3VjaCBhcyBtaXNzaW5nIHZhbHVlcywgb3V0bGllcnMsIG9yIHVudXNlZCBjYXRlZ29yaWVzLCBpbXByb3Zpbmcgb3ZlcmFsbCBkYXRhIHF1YWxpdHkNCjo6Og0KDQo6OjogZmxhZw0KVGhlIG9yaWdpbmFsIGRhdGFzZXQgaXMgdmVyeSBsYXJnZS0tLWl0IGluY2x1ZGVzIDg0OCB2YXJpYWJsZXMgYW5kIDE3LDAwMCsgb2JzZXJ2YXRpb25zLg0KOjo6DQoNClRoZSBkYXRhc2V0IHdlIGhhdmUgdXNlZCBpbiBwcmV2aW91cyBzZXNzaW9ucyBvcmlnaW5hdGVkIGZyb20gdGhlIFtHZW5lcmFsIFNvY2lhbCBTdXJ2ZXksIEN5Y2xlIDI5ICgyMDE1KV0gKGh0dHBzOi8vb2Rlc2kuY2EvZW4vZGV0YWlscz9pZD0vb2Rlc2kvZG9pX18xMC01NjgzX1NQM19SRFMwQ0sueG1sKSwgZnJvbSB0aGUgU29jaWFsIGFuZCBBYm9yaWdpbmFsIFN0YXRpc3RpY3MgRGl2aXNpb24gYXQgU3RhdGlzdGljcyBDYW5hZGEuIFRoaXMgc3VydmV5IHRyYWNrcyBob3cgQ2FuYWRpYW5zIHNwZW5kIGFuZCBtYW5hZ2UgdGhlaXIgdGltZSwgaGVscGluZyB1cyB1bmRlcnN0YW5kIHBhdHRlcm5zIHRpZWQgdG8gd2VsbC1iZWluZyBhbmQgc3RyZXNzLiBIb3dldmVyLCB0aGUgb3JpZ2luYWwgZGF0YXNldCBpcyB2ZXJ5IGxhcmdlLS0taXQgaW5jbHVkZXMgb3ZlciA4NDggdmFyaWFibGVzIGFuZCBtb3JlIHRoYW4gMTcsMDAwIG9ic2VydmF0aW9ucy4NCg0KRm9yIHRoZSBwdXJwb3NlcyBvZiB0aGlzIHR1dG9yaWFsLCB3ZSBhcmUgbm90IGludGVyZXN0ZWQgaW4gZXZlcnkgdmFyaWFibGUuIEluc3RlYWQsIHdlIGFyZSB3b3JraW5nIHdpdGggYSAqKnN1YnNldCoqIG9mIHRoaXMgZGF0YXNldDogMjkgdmFyaWFibGVzIGZvY3VzZWQgbWFpbmx5IG9uIHRpbWUgZHVyYXRpb25zIGFuZCBrZXkgZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzLiBUaGlzIG1ha2VzIHRoZSBkYXRhIG1vcmUgbWFuYWdlYWJsZSBhbmQgcmVsZXZhbnQgZm9yIG91ciBleHBsb3JhdGlvbiBvZiB0aW1lIHVzZSBhbmQgcGVyY2VwdGlvbnMgb2YgdGltZSBwcmVzc3VyZS4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjxoNCBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyI+DQoNCkZyYW1pbmcgdGhlIEd1aWRpbmcgUXVlc3Rpb246IFdobyBGZWVscyBSdXNoZWQ/DQoNCjwvaDQ+DQoNCjwhLS0gIyMjIEZyYW1pbmcgdGhlIEd1aWRpbmcgUXVlc3Rpb246IFdobyBGZWVscyBSdXNoZWQ/IC0tPg0KDQpMZXQncyByZXR1cm4gdG8gdGhlIHRpbWUgdXNhZ2UgZGF0YXNldC4gSW4gdGhpcyBzZWN0aW9uLCBzdXBwb3NlIHdlJ3JlIGludGVyZXN0ZWQgaW4gdW5kZXJzdGFuZGluZyBob3cgcGVvcGxlIHdobyBmZWVsIHJ1c2hlZCBzcGVuZCB0aGVpciB0aW1lIGRpZmZlcmVudGx5IGZyb20gdGhvc2Ugd2hvIGRvbid0LiBUbyBhbnN3ZXIgdGhpcyBxdWVzdGlvbiwgd2UgZG9uJ3QgbmVlZCBldmVyeSBzaW5nbGUgcm93IG9yIGNvbHVtbiwgYXMgd29ya2luZyB3aXRoIHRoZSBkYXRhIGluIGl0cyBvcmlnaW5hbCBmb3JtYXQgd291bGQgYmUgdW5uZWNlc3NhcmlseSBjb21wbGV4LiBJbnN0ZWFkLCB3ZSBuZWVkIHRvICJnZXQgaW5zaWRlIiBvdXIgZGF0YSBieSBzdWJzZXR0aW5nOiBmaWx0ZXJpbmcgdGhlIHJvd3MgYW5kIHNlbGVjdGluZyB0aGUgY29sdW1ucyB0aGF0IG1hdHRlci4NCg0KV2Ugd2lsbCBleHBsb3JlIG91ciBkYXRhc2V0IHRocm91Z2ggb25lIGd1aWRpbmcgcXVlc3Rpb246DQoNCioqSG93IGRvIHBlb3BsZSB3aG8gZmVlbCBydXNoZWQgc3BlbmQgdGhlaXIgdGltZSBkaWZmZXJlbnRseSBmcm9tIHRob3NlIHdobyBkb24ndD8qKg0KDQpXZSdsbCBmb2N1cyBvbiB0aGUgcmVsZXZhbnQgcm93cyBhbmQgY29sdW1ucyB0aGF0IGFuc3dlciB0aGlzIHF1ZXN0aW9uLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgU3RlcCAwOiBTZXR1cCBhbmQgTG9hZCB0aGUgRGF0YQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQ0KYGBgDQoNCjxoNCBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyI+DQoNCkxvYWQgYW5kIFVuZGVyc3RhbmQgdGhlIERhdGFzZXQNCg0KPC9oND4NCg0KPCEtLSAjIyMgTG9hZCBhbmQgVW5kZXJzdGFuZCB0aGUgRGF0YXNldCAgLS0+DQoNCkZpcnN0LCB3ZSBuZWVkIHRvIGxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXMgZm9yIHRoaXMgc2Vzc2lvbi4NCg0KYGBge3IsIGRhdGEtaXNvbGF0aW9uLTE1LCByZXN1bHRzID0gJ2hpZGUnLCBlY2hvPUZBTFNFfQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KYGBgDQoNCmBgYHtyLCBsaWJyYXJpZXN9DQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCkluIG91ciBzdWJzZXF1ZW50IHRhc2tzLCB0aGUgYGRwbHlyYCBwYWNrYWdlIHdpbGwgYmUgZXNzZW50aWFsIGZvciBzdWJzZXR0aW5nIG9wZXJhdGlvbnMgc3VjaCBhcyBmaWx0ZXJpbmcgcm93cyBhbmQgc2VsZWN0aW5nIGNvbHVtbnMuIFdlIGNhbiBlaXRoZXIgY29udGludWUgdXNpbmcgdGhlIGBqc19kYXRhYCBvYmplY3QgZnJvbSB0aGUgcHJldmlvdXMgc2VjdGlvbiBvciBsb2FkIHRoZSBgdGltZXVzZV9kYXkzXzEuUmRhdGFgIGZpbGUgZnJvbSB0aGUgZGF0YSBmb2xkZXIuDQoNCmBgYHtyfQ0KbG9hZCgiZGF0YS90aW1ldXNlX2NsZWFuLWNvbHNfcmVjb2RlZC5SRGF0YSIpDQpgYGANCg0KTm93LCBsZXQncyBhZ2FpbiBleGFtaW5lIG91ciBkYXRhc2V0IHN0cnVjdHVyZSBieSBkaXNwbGF5aW5nIHRoZSBmaXJzdCBmZXcgcm93cyBieSB1c2luZyBgaGVhZCgpYCBmdW5jdGlvbi4NCg0KYGBge3IsIGRhdGEtaXNvbGF0aW9uLTEsIHJlc3VsdHMgPSAnaGlkZSd9DQpqc19kYXRhIHw+DQogIGhlYWQoKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KanNfZGF0YSB8Pg0KICBoZWFkKCkgfD4NCiAga2JsKCkgfD4NCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4NCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCmBgYA0KDQpBcyB3ZSBjYW4gc2VlLCB0aGVyZSBhcmUgMzAgY29sdW1ucyBpbiB0aGUgZGF0YXNldCwgd2hpY2ggaXMgYSBsb3QgdG8gd29yayB3aXRoLiBGb3Igbm93LCB3ZSBjYW4gZm9jdXMgb24gdGhlIGZvbGxvd2luZyBrZXkgY29sdW1uczoNCg0KLSAgIGBpZGA6IFJlY29yZCBpZGVudGlmaWNhdGlvbg0KLSAgIGBhZ2VHcnBgOiBBZ2UgZ3JvdXAgb2YgcmVzcG9uZGVudCAoZ3JvdXBzIG9mIDEwKQ0KLSAgIGBzZXhgOiBTZXggb2YgcmVzcG9uZGVudA0KLSAgIGBtYXJpdGFsU3RhdGA6IE1hcml0YWwgc3RhdHVzIG9mIHRoZSByZXNwb25kZW50DQotICAgYHByb3ZpbmNlYDogUHJvdmluY2Ugb2YgcmVzaWRlbmNlDQotICAgYHBvcENlbnRlcmA6IFBvcHVsYXRpb24gY2VudHJlIGluZGljYXRvcg0KLSAgIGBlZHVMZXZlbGA6IEVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgKGhpZ2hlc3QgZGVncmVlKQ0KLSAgIGBmZWVsUnVzaGVkYDogR2VuZXJhbCB0aW1lIHVzZSAtLSBGZWVsIHJ1c2hlZA0KLSAgIGBleHRyYVRpbWVgOiBHZW5lcmFsIHRpbWUgdXNlIC0tIEV4dHJhIHRpbWUNCi0gICBgZHVyU2xlZXBgOiBEdXJhdGlvbiAtLSBTbGVlcGluZywgcmVzdGluZywgcmVsYXhpbmcsIHNpY2sgaW4gYmVkDQotICAgYGR1cldvcmtgOiBEdXJhdGlvbiAtLSBQYWlkIHdvcmsNCi0gICBgdGltZVdvcmthaG9saWNgOiBQZXJjZXB0aW9ucyBvZiB0aW1lIC0tIFdvcmthaG9saWMNCi0gICBgdGltZVdhbnRBbG9uZWA6IFBlcmNlcHRpb25zIG9mIHRpbWUgLS0gV291bGQgbGlrZSBtb3JlIHRpbWUgYWxvbmUNCg0KVGhlc2UgY29sdW1ucyBwcm92aWRlIGluZm9ybWF0aW9uIG9uIGRlbW9ncmFwaGljcywgdGltZSB1c2FnZSwgYW5kIHRpbWUgcGVyY2VwdGlvbnMuIFdlIGNhbiB1c2UgdGhlbSB0byBleHBsb3JlIHBhdHRlcm5zIGluIHdvcmstbGlmZSBiYWxhbmNlLCBlZHVjYXRpb24sIGFuZCBzb2NpYWwgdGltZS4NCg0KTm93IHRoYXQgd2UgdW5kZXJzdGFuZCBvdXIgZGF0YXNldCBzdHJ1Y3R1cmUsIGxldCdzIG1vdmUgb24gdG8gbGVhcm5pbmcgaG93IHRvIGZpbHRlciBhbmQgbWFuaXB1bGF0ZSB0aGlzIGRhdGEgZWZmZWN0aXZlbHkgdXNpbmcgQm9vbGVhbiBvcGVyYXRvcnMgaW4gYGRwbHlyYC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFN0ZXAgMTogTGVhcm5pbmcgQWJvdXQgRmlsdGVyaW5nDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQo8aDQgc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPg0KDQpDb25kaXRpb25hbCBGaWx0ZXJpbmcgd2l0aCBCb29sZWFuIE9wZXJhdG9ycyB1c2luZyA8c3Ryb25nPmRwbHlyPC9zdHJvbmc+DQoNCjwvaDQ+DQoNCjwhLS0gIyMjIENvbmRpdGlvbmFsIEZpbHRlcmluZyB3aXRoIEJvb2xlYW4gT3BlcmF0b3JzIHVzaW5nICoqZHBseXIqKiAtLT4NCg0KYGRwbHlyYCBpcyBhIHBvd2VyZnVsIHBhY2thZ2UgdGhhdCBsZXRzIHVzIGV4dHJhY3QgYW5kIHRyYW5zZm9ybSBkYXRhIHdpdGggYSBjbGVhciwgcmVhZGFibGUgc3ludGF4LiBJbiBgZHBseXJgLCB3ZSB1c2UgZnVuY3Rpb25zIGxpa2UgYGZpbHRlcigpYCwgYHNlbGVjdCgpYCwgYW5kIGBtdXRhdGUoKWAgdG8gd29yayB3aXRoIG91ciBkYXRhLiBCb29sZWFuIG9wZXJhdG9ycyAoYD09YCwgYDxgLCBgPmAsIGA8PWAsIGA+PWAsIGFuZCBgIT1gKSBhcmUgdXNlZCB3aXRoaW4gdGhlc2UgZnVuY3Rpb25zIHRvIHRlc3QgY29uZGl0aW9ucywgYW5kIHdlIGNhbiBjb21iaW5lIGNvbmRpdGlvbnMgd2l0aCBgJmAgKGFuZCkgb3IgYHxgIChvcikuDQoNCkxldCdzIHN0YXJ0IGJ5IHVuZGVyc3RhbmRpbmcgdGhlIGJhc2ljIGNvbXBhcmlzb24gb3BlcmF0b3JzIHRoYXQgd2lsbCBoZWxwIHVzIGNyZWF0ZSBmaWx0ZXJpbmcgY29uZGl0aW9ucy4NCg0KIyMjIyAqKkNvbXBhcmlzb24gT3BlcmF0b3JzKioNCg0KQ29tcGFyaXNvbiBvcGVyYXRvcnMgYWxsb3cgdXMgdG8gY2hlY2sgY29uZGl0aW9ucyB3aXRoaW4gb3VyIGRhdGFzZXQuIFRoZXNlIHJldHVybiBgVFJVRWAgb3IgYEZBTFNFYCBiYXNlZCBvbiB3aGV0aGVyIHRoZSBjb25kaXRpb24gaXMgbWV0Lg0KDQp8IE9wZXJhdG9yIHwgTWVhbmluZyAgICAgICAgICAgICAgICAgIHwgRXhhbXBsZSAgfCBSZXN1bHQgfA0KfC0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLXwtLS0tLS0tLXwNCnwgYD09YCAgICAgfCBFcXVhbCB0byAgICAgICAgICAgICAgICAgfCBgNSA9PSA1YCB8IGBUUlVFYCB8DQp8IGAhPWAgICAgIHwgTm90IGVxdWFsIHRvICAgICAgICAgICAgIHwgYDUgIT0gM2AgfCBgVFJVRWAgfA0KfCBgPGAgICAgICB8IExlc3MgdGhhbiAgICAgICAgICAgICAgICB8IGAzIDwgNWAgIHwgYFRSVUVgIHwNCnwgYD5gICAgICAgfCBHcmVhdGVyIHRoYW4gICAgICAgICAgICAgfCBgNSA+IDNgICB8IGBUUlVFYCB8DQp8IGA8PWAgICAgIHwgTGVzcyB0aGFuIG9yIGVxdWFsIHRvICAgIHwgYDMgPD0gM2AgfCBgVFJVRWAgfA0KfCBgPj1gICAgICB8IEdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byB8IGA1ID49IDNgIHwgYFRSVUVgIHwNCg0KT25jZSB3ZSB1bmRlcnN0YW5kIHRoZXNlIGJhc2ljIGNvbXBhcmlzb24gb3BlcmF0b3JzLCB3ZSBjYW4gY29tYmluZSB0aGVtIHVzaW5nIGxvZ2ljYWwgb3BlcmF0b3JzIHRvIGNyZWF0ZSBtb3JlIGNvbXBsZXggZmlsdGVyaW5nIGNvbmRpdGlvbnMuDQoNCiMjIyMgKipMb2dpY2FsIE9wZXJhdG9ycyoqDQoNCkxvZ2ljYWwgb3BlcmF0b3JzIGFsbG93IHVzIHRvIGZpbHRlciBkYXRhIGJhc2VkIG9uIG11bHRpcGxlIGNvbmRpdGlvbnMuDQoNCnwgT3BlcmF0b3IgfCBNZWFuaW5nICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBFeGFtcGxlICAgICAgICAgICAgIHwgUmVzdWx0ICB8DQp8LS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLXwNCnwgYCZgICAgICAgfCBMb2dpY2FsIEFORCAoQm90aCBjb25kaXRpb25zIG11c3QgYmUgVFJVRSkgICAgICAgfCBgKDUgPiAzKSAmICg0IDwgNilgIHwgYFRSVUVgICB8DQp8IGB8YCAgICAgIHwgTG9naWNhbCBPUiAoQXQgbGVhc3Qgb25lIGNvbmRpdGlvbiBtdXN0IGJlIFRSVUUpIHwgYCg1ID4gMykgfCAoNCA+IDYpYCB8IGBUUlVFYCAgfA0KfCBgIWAgICAgICB8IExvZ2ljYWwgTk9UIChSZXZlcnNlcyBUUlVFL0ZBTFNFKSAgICAgICAgICAgICAgICB8IGAhKDUgPiAzKWAgICAgICAgICAgfCBgRkFMU0VgIHwNCg0KTGV0J3MgdHJ5IHVzaW5nIHRoZXNlIGxvZ2ljYWwgb3BlcmF0b3JzIHRvIGZpbHRlciB0aGUgcm93cyBpbiB0aGUgZm9sbG93aW5nIGV4YW1wbGUuDQoNCldlIHdhbnQgdG8gdW5kZXJzdGFuZCB3aGV0aGVyIGZlZWxpbmcgcnVzaGVkIG1pZ2h0IHJlbGF0ZSB0byBob3cgbXVjaCB0aW1lIGlzIHNwZW50IG9uIHdvcmssIHNsZWVwLCBvciBhbG9uZSB0aW1lLiBUaGVyZWZvcmUsIHRoZSBjb2x1bW5zIG9mIGludGVyZXN0IGFyZSBgZmVlbFJ1c2hlZGAsIGBkdXJTbGVlcGAsIGBkdXJXb3JrYCwgYW5kIGBkdXJBbG9uZWAuIEZpcnN0LCBsZXQncyBleHBsb3JlIHRoZSB2YWx1ZXMgaW4gdGhlc2UgY29sdW1ucyB1c2luZyBgZHBseXJgLg0KDQpMZXQncyBsb29rIGF0IHRoZSB1bmlxdWUgdmFsdWVzIGluIHRoZSBgZmVlbFJ1c2hlZGAgY29sdW1uIHRvIHVuZGVyc3RhbmQgd2hhdCBjYXRlZ29yaWVzIGV4aXN0IGluIG91ciBkYXRhIGJlZm9yZSB3ZSBzdGFydCBmaWx0ZXJpbmcgYmFzZWQgb24gdGhlc2UgdmFsdWVzLg0KDQpgYGB7cn0NCmpzX2RhdGEgfD4gDQogIGRpc3RpbmN0KGZlZWxSdXNoZWQpDQpgYGANCg0KU2luY2Ugb3VyIGR1cmF0aW9uIGNvbHVtbnMgKGBkdXJTbGVlcGAsIGBkdXJXb3JrYCwgYW5kIGBkdXJBbG9uZWApIGFyZSBjb250aW51b3VzIG51bWVyaWNhbCB2YXJpYWJsZXMgcmF0aGVyIHRoYW4gY2F0ZWdvcmljYWwsIGl0J3MgbW9yZSBpbmZvcm1hdGl2ZSB0byBleGFtaW5lIHRoZWlyIGRpc3RyaWJ1dGlvbnMgcmF0aGVyIHRoYW4ganVzdCB0aGVpciB1bmlxdWUgdmFsdWVzLiBMb29raW5nIGF0IHRoZSBkaXN0cmlidXRpb24gaGVscHMgdXMgdW5kZXJzdGFuZCB3aGljaCB2YWx1ZXMgYXJlIGNvbW1vbiwgaWRlbnRpZnkgcGF0dGVybnMsIGFuZCBzcG90IHBvdGVudGlhbCBvdXRsaWVyczoNCg0KYGBge3J9DQpqc19kYXRhIHw+IA0KICBjb3VudChkdXJTbGVlcCkgfD4gDQogIGFycmFuZ2UoZGVzYyhuKSkgfD4gDQogIGhlYWQoMTApIA0KYGBgDQoNCmBgYHtyfQ0KanNfZGF0YSB8PiANCiAgY291bnQoZHVyV29yaykgfD4gDQogIGFycmFuZ2UoZGVzYyhuKSkgfD4gDQogIGhlYWQoMTApDQpgYGANCg0KYGBge3J9DQpqc19kYXRhIHw+IA0KICBjb3VudChkdXJBbG9uZSkgfD4gDQogIGFycmFuZ2UoZGVzYyhuKSkgfD4gDQogIGhlYWQoMTApIA0KYGBgDQoNCjo6OiB3YWxrdGhyb3VnaA0KVGhlc2UgY29tbWFuZHMgd29yayB0b2dldGhlciB0byBzaG93IHRoZSBtb3N0IGNvbW1vbiB0aW1lIHBhdHRlcm5zOg0KDQotICAgYGNvdW50KClgIHRhbGxpZXMgZWFjaCBkdXJhdGlvbiB2YWx1ZSwNCi0gICBgYXJyYW5nZShkZXNjKG4pKWAgc29ydHMgZnJvbSBoaWdoZXN0IHRvIGxvd2VzdCBmcmVxdWVuY3ksDQotICAgYGhlYWQoMTApYCBrZWVwcyBvbmx5IHRoZSB0b3AgMTAgcmVzdWx0cw0KDQpUaGlzIHF1aWNrIG92ZXJ2aWV3IGhlbHBzIHVzIHVuZGVyc3RhbmQgdHlwaWNhbCB0aW1lIGFsbG9jYXRpb24gcGF0dGVybnMgZm9yIHNsZWVwLCB3b3JrLCBhbmQgYmVpbmcgYWxvbmUuDQo6OjoNCg0KSXQgYXBwZWFycyB0aGF0IGFsbCB0aGUgY29sdW1ucyBjb250YWluIG51bWVyaWMgdmFsdWVzLiBIb3dldmVyLCBhcyB3ZSBsb29rIGF0IHRoZSBkYXRhIGRpY3Rpb25hcnksIHdlJ2xsIHNlZSB0aGF0IG9ubHkgYGR1cldvcmtgLCBgZHVyU2xlZXBgIGFuZCBgZHVyQWxvbmVgIGhhdmUgbnVtZXJpYyB2YWx1ZXMgdGhhdCByZXByZXNlbnQgcmVhbCBxdWFudGl0aWVzIChtaW51dGVzIG9mIHdvcmssIHNsZWVwIG9yIHRpbWUgYWxvbmUgaW4gYSBkYXkpLiBJbiBjb250cmFzdCwgdGhlIHZhbHVlcyBpbiB0aGUgYGZlZWxSdXNoZWRgIGNvbHVtbiBoYXZlIGEgZGlmZmVyZW50IG1lYW5pbmcuDQoNCnwgQ29kZSB8IFZhbHVlICAgICAgICAgICAgfA0KfC0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS18DQp8IDEgICAgfCBkYWlseSAgICAgICAgICAgIHwNCnwgMiAgICB8IGZldyBUaW1lcyBhIFdlZWsgfA0KfCAzICAgIHwgb25jZSBhIFdlZWsgICAgICB8DQp8IDQgICAgfCBvbmNlIGEgTW9udGggICAgIHwNCnwgNSAgICB8IGxlc3MgYSBNb250aCAgICAgfA0KfCA2ICAgIHwgbmV2ZXIgICAgICAgICAgICB8DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBTdGVwIDI6IEFwcGx5IEJhc2ljIEZpbHRlcmluZw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KPCEtLSAjIyMgRmlsdGVyaW5nIFJvd3Mgd2l0aCAqZHBseXIqIC0tPg0KDQo8aDQgc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPg0KDQpGaWx0ZXJpbmcgUm93cyB3aXRoIDxzdHJvbmc+ZHBseXI8L3N0cm9uZz4NCg0KPC9oND4NCg0KIVtdKGltYWdlcy8zLUFDVC0zLUZpbHRlci5wbmcpDQoNCkZvciB0aGlzIGFuYWx5c2lzLCB3ZSB3aWxsIGNvbnNpZGVyIHJlc3BvbmRlbnRzIHdobyByZXBvcnQgZmVlbGluZyBydXNoZWQgYXMgdGhvc2Ugd2hvc2UgZnJlcXVlbmN5IG9mIGZlZWxpbmcgdGhpcyB3YXkgaXMgYXQgbGVhc3Qgb25jZSBhIHdlZWssIGFuZCB0aG9zZSB3aG8gZmVlbCB0aGlzIHdheSBsZXNzIHRoYW4gb25jZSBhIHdlZWsgYXMgbm90IGZlZWxpbmcgcnVzaGVkLg0KDQojIyMjIEV4dHJhY3RpbmcgUmVzcG9uZGVudHMgV2hvIERvIG9yIERvIG5vdCBGZWVsIFJ1c2hlZCBEYWlseQ0KDQpGaXJzdCwgbGV0J3MgZXh0cmFjdCBvbmx5IHRoZSByZXNwb25kZW50cyB3aG8gcmVwb3J0IGZlZWxpbmcgcnVzaGVkIGRhaWx5LiBMb29raW5nIGF0IHRoZSB0YWJsZSwgdGhlIHZhbHVlIGNvcnJlc3BvbmRpbmcgdG8gZmVlbGluZyBydXNoZWQgZGFpbHkgaXMgMS4gV2UgY2FuIGZpbHRlciBmb3IgdGhlc2UgcmVzcG9uZGVudHMgdXNpbmcgYGZpbHRlcigpYC4NCg0KOjo6IG5vdGUNClRoZSBgZmlsdGVyKClgIGZ1bmN0aW9uIGluIGBkcGx5cmAgcmV0dXJucyBvbmx5IHRoZSByb3dzIHRoYXQgbWVldCBhIHNwZWNpZmllZCBjb25kaXRpb24uDQo6OjoNCg0KV2UgZmlsdGVyIGFueSByb3cgd2hpY2ggaGFzIGBmZWVsUnVzaGVkYCBlcXVhbCB0byAxIGJ5IHBhc3NpbmcgdGhlIEJvb2xlYW4gZXhwcmVzc2lvbiBgZmVlbFJ1c2hlZCA9PSAxYCBpbnRvIGBmaWx0ZXJgLCBhbmQgdGhlbiB1c2UgYGhlYWQoKWAgdG8gc2VlIHdoYXQgdGhlIGZpbHRlcmVkIGRhdGEgbG9vayBsaWtlLg0KDQpgYGB7ciwgZGF0YS1pc29sYXRpb24tMiwgcmVzdWx0cyA9ICdoaWRlJ30NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkID09IDEpIHw+IA0KICBoZWFkKCkNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkID09IDEpIHw+IA0KICBoZWFkKCkgfD4NCiAga2JsKCkgfD4NCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4NCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCmBgYA0KDQpMb29raW5nIGF0IHRoZSBmaWx0ZXJlZCBkYXRhLCB3ZSBjYW4gY29uZmlybSB0aGF0IG91ciBmaWx0ZXIgd29ya2VkIGNvcnJlY3RseS0tLWFsbCByb3dzIHNob3cgYGZlZWxSdXNoZWRgIGVxdWFsIHRvIDEgKG1lYW5pbmcgdGhleSBmZWVsIHJ1c2hlZCBkYWlseSkuDQoNClVzaW5nIGBmaWx0ZXIoKWAsIHdlIHN1YnNldCB0aGUgZGF0YSB0byBrZWVwIG9ubHkgdGhvc2Ugcm93cyB3aGVyZSB0aGUgY29uZGl0aW9uIGlzIG1ldC4gSW4gdGhlIGFib3ZlIGV4YW1wbGUsIHRoZSBjb25kaXRpb24gYGZlZWxSdXNoZWQgPT0gMWAgY3JlYXRlcyBhIGxvZ2ljYWwgdmVjdG9yIHRoYXQgaXMgYFRSVUVgIGZvciByb3dzIHdoZXJlIHRoZSB2YWx1ZSBlcXVhbHMgMSAoY29ycmVzcG9uZGluZyB0byAiZGFpbHkiKS4NCg0KIyMjIyBVc2luZyBDb21wYXJpc29uIGFuZCBMb2dpY2FsIE9wZXJhdG9ycw0KDQpOb3Qgb25seSBjYW4gd2UgdXNlIHRoZSBgPT1gIG9wZXJhdG9yIHRvIHRlc3QgZm9yIGVxdWFsaXR5LCBidXQgd2UgY2FuIGFsc28gdXNlIG9wZXJhdG9ycyBzdWNoIGFzIGA8YCwgYD5gLCBgPD1gLCBgPj1gLCBhbmQgYCE9YCB0byBjb21wYXJlIHZhbHVlcy4gRm9yIGV4YW1wbGUsIHdlIG1pZ2h0IGZpbHRlciByb3dzIHdoZXJlIGEgbnVtZXJpYyB2YXJpYWJsZSBleGNlZWRzIGEgY2VydGFpbiB0aHJlc2hvbGQsIGlzIGJlbG93IGEgbGltaXQsIG9yIGlzIG5vdCBlcXVhbCB0byBhIHNwZWNpZmllZCB2YWx1ZS4gTGV0J3MgdXNlIGFuIGV4YW1wbGUgZnJvbSB0aGUgYGR1clNsZWVwYCBjb2x1bW4uDQoNCkZvciBpbnN0YW5jZSwgaWYgd2Ugd2FudCB0byBmaWx0ZXIgcm93cyBmb3IgdGhlIGBkdXJTbGVlcGAgY29sdW1uIHRvIGNhcHR1cmUgYW55IGluc3RhbmNlcyB3aXRoIHNsZWVwIGR1cmF0aW9uIGxlc3MgdGhhbiBgNjAwYCwgd2UgY2FuIHdyaXRlOg0KDQpgYGB7ciwgZGF0YS1pc29sYXRpb24tMywgcmVzdWx0cyA9ICdoaWRlJ30NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihkdXJTbGVlcCA8IDYwMCkgfD4NCiAgaGVhZCgpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpqc19kYXRhIHw+IA0KICBmaWx0ZXIoZHVyU2xlZXAgPCA2MDApIHw+DQogIGhlYWQoKSB8Pg0KICBrYmwoKSB8Pg0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8Pg0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KYGBgDQoNCkFsdGVybmF0aXZlbHksIHdlIGNhbiBmaWx0ZXIgcm93cyB3aGVyZSBgZHVyU2xlZXBgIGlzIGJldHdlZW4gYDYwMGAgYW5kIGAxMDAwYC4gVG8gZG8gdGhpcywgd2UgY2hhaW4gdHdvIGNvbmRpdGlvbnMgdXNpbmcgdGhlIGAmYCBvcGVyYXRvcjoNCg0KYGBge3IsIGRhdGEtaXNvbGF0aW9uLTQsIHJlc3VsdHMgPSAnaGlkZSd9DQpqc19kYXRhIHw+IA0KICBmaWx0ZXIoZHVyU2xlZXAgPj0gNjAwICYgZHVyU2xlZXAgPD0gMTAwMCkgfD4NCiAgaGVhZCgpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpqc19kYXRhIHw+IA0KICBmaWx0ZXIoZHVyU2xlZXAgPj0gNjAwICYgZHVyU2xlZXAgPD0gMTAwMCkgfD4NCiAgaGVhZCgpIHw+DQogIGtibCgpIHw+DQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+DQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KOjo6IHdhbGt0aHJvdWdoDQpJbiB0aGlzIGV4YW1wbGU6DQoNCi0gICBUaGUgY29uZGl0aW9uIGBkdXJTbGVlcCA+PSA2MDBgIGNoZWNrcyBmb3Igcm93cyB3aGVyZSBzbGVlcCBkdXJhdGlvbiBpcyBhdCBsZWFzdCBgNjAwYC4NCi0gICBUaGUgY29uZGl0aW9uIGBkdXJTbGVlcCA8PSAxMDAwYCBjaGVja3MgZm9yIHJvd3Mgd2hlcmUgc2xlZXAgZHVyYXRpb24gaXMgYXQgbW9zdCBgMTAwMGAuDQotICAgVGhlIGAmYCBvcGVyYXRvciBjb21iaW5lcyB0aGVzZSBjb25kaXRpb25zLCBlbnN1cmluZyB0aGF0IG9ubHkgcm93cyBzYXRpc2Z5aW5nIGJvdGggY29uZGl0aW9ucyBhcmUgcmV0dXJuZWQuDQo6OjoNCg0KSGVyZSB3ZSd2ZSB1c2VkIHRoZSBgJmAgb3BlcmF0b3IgZm9yICJhbmQiIGNvbmRpdGlvbnMsIGJ1dCB3ZSBjYW4gYWxzbyB1c2UgdGhlIGB8YCBvcGVyYXRvciB0byBzcGVjaWZ5ICJvciIgY29uZGl0aW9ucy4gQnkgdXNpbmcgdGhlc2UgYm9vbGVhbiBvcGVyYXRvcnMsIHdlIGNhbiBjaGFpbiBtdWx0aXBsZSBjb25kaXRpb25zIHRvZ2V0aGVyLiANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIFN0ZXAgMzogQXBwbHkgQ29tcGxleCBGaWx0ZXJpbmcNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjwhLS0gIyMjIENvbXBsZXggRmlsdGVyaW5nIHdpdGggZHBseXI6IEZpbHRlcmluZyBSb3dzIGZvciBUaG9zZSBXaG8gRmVlbCBSdXNoZWQgLS0+DQoNCjxoNCBzdHlsZT0idGV4dC1hbGlnbjpsZWZ0OyI+DQoNCkNvbXBsZXggRmlsdGVyaW5nIHdpdGggPHN0cm9uZz5kcGx5cjwvc3Ryb25nPjogRmlsdGVyaW5nIFJvd3MgZm9yIFRob3NlIFdobyBGZWVsIFJ1c2hlZA0KDQo8L2g0Pg0KDQpOb3csIGxldCdzIHBlcmZvcm0gYSBtb3JlIGNvbXBsZXggZmlsdGVyaW5nLiBTdXBwb3NlIHdlIHdhbnQgdG8gY2FwdHVyZSByZXNwb25kZW50cyB3aG8gZmVlbCBydXNoZWQgZnJlcXVlbnRseS0tLXRoYXQgaXMsIHRob3NlIHdob3NlIGBmZWVsUnVzaGVkYCB2YWx1ZSBpcyBlaXRoZXIgYDFgLCBgMmAsIG9yIGAzYC0tLWFuZCB0aG9zZSB3aG8gZG8gbm90IGZlZWwgcnVzaGVkIGZyZXF1ZW50bHksIG1lYW5pbmcgdGhvc2Ugd2hvc2UgYGZlZWxSdXNoZWRgIHZhbHVlIGlzIGVpdGhlciBgNGAsIGA1YCwgb3IgYDZgLg0KDQpUaGVyZSBhcmUgbXVsdGlwbGUgd2F5cyB0byBmaWx0ZXIgcm93cyB0aGF0IG1lZXQgb25lIG9mIHRoZXNlIGNvbmRpdGlvbnMuDQoNCiMjIyMgVXNpbmcgUmFuZ2UgQ29tcGFyaXNvbg0KDQpUaGUgZmlyc3QgbWV0aG9kIHVzZXMgYSByYW5nZSBjb21wYXJpc29uIHdpdGggYDw9YCB0byBmaWx0ZXIgdGhlIHJvd3MuDQoNCmBgYHtyLCBkYXRhLWlzb2xhdGlvbi01LCByZXN1bHRzID0gJ2hpZGUnfQ0KanNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgPD0gMykgfD4NCiAgaGVhZCgpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpqc19kYXRhIHw+IA0KICBmaWx0ZXIoZmVlbFJ1c2hlZCA8PSAzKSB8Pg0KICBoZWFkKCkgfD4NCiAga2JsKCkgfD4NCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4NCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCg0KYGBgDQoNCmBgYHtyLCBkYXRhLWlzb2xhdGlvbi02LCByZXN1bHRzID0gJ2hpZGUnfQ0KanNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgPiAzICYgZmVlbFJ1c2hlZCA8PSA2KSB8Pg0KICBoZWFkKCkNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkID4gMyAmIGZlZWxSdXNoZWQgPD0gNikgfD4NCiAgaGVhZCgpIHw+DQogIGtibCgpIHw+DQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+DQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KTm93IGxldCdzIGNhbGN1bGF0ZSBob3cgbWFueSByb3dzIHJlbWFpbiBhZnRlciB3ZSBwZXJmb3JtZWQgZmlsdGVyaW5nLg0KDQpgYGB7cn0NCmFsbF9yb3dzIDwtIGpzX2RhdGEgfD4gDQogIG5yb3coKQ0KcnVzaGVkX3Jvd3MgPC0ganNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgPD0gMykgfD4NCiAgbnJvdygpDQpub3RfcnVzaGVkX3Jvd3MgPC0ganNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgPiAzICYgZmVlbFJ1c2hlZCA8PSA2KSB8Pg0KICBucm93KCkNCmBgYA0KDQpMZXQncyBwcmludCBhbmQgc2VlIHRoZSBudW1iZXIgb2Ygcm93cyBpbiBlYWNoIGRhdGFmcmFtZSBhZnRlciBmaWx0ZXJpbmcuDQoNCmBgYHtyfQ0KcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2Ygcm93cyBpbiBkYXRhIGlzOiIsIGFsbF9yb3dzKSkNCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHJvd3MgaW4gcnVzaGVkIGlzOiIsIHJ1c2hlZF9yb3dzKSkNCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHJvd3MgaW4gbm90IHJ1c2hlZCBpczoiLCBub3RfcnVzaGVkX3Jvd3MpKQ0KYGBgDQoNCjo6OiB3YWxrdGhyb3VnaA0KLSAgIFRoaXMgYXBwcm9hY2ggc2VsZWN0cyByb3dzIHdoZXJlIGBmZWVsUnVzaGVkYCBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gYDNgIChpLmUuLCB2YWx1ZXMgYDFgLCBgMmAsIG9yIGAzYCkgdG8gaW5kaWNhdGUgcmVzcG9uZGVudHMgd2hvIGZlZWwgcnVzaGVkLg0KLSAgIFJvd3Mgd2hlcmUgYGZlZWxSdXNoZWRgIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byBgNGAgYW5kIGxlc3MgdGhhbiBvciBlcXVhbCB0byBgNmAgYXJlIGNvbnNpZGVyZWQgbm90IHJ1c2hlZC4NCi0gICBXZSB1c2UgdGhlIGAmYCBvcGVyYXRvciB0byBjaGFpbiB0aGUgdHdvIEJvb2xlYW4gb3BlcmF0aW9ucy4NCi0gICBUaGUgYG5yb3coKWAgZnVuY3Rpb24gY2FsY3VsYXRlcyB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIGRhdGEgZnJhbWUuDQo6OjoNCg0KQXMgd2UgY2FuIHNlZSwgdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUgaGFzIGAxNywzOTBgIHJvd3M7IGFmdGVyIGZpbHRlcmluZywgdGhlIHJ1c2hlZCBkYXRhIGZyYW1lIGNvbnRhaW5zIG9ubHkgYDEyLDY4OWAgcm93cy4NCg0KIyMjIyBDaGFpbmluZyBNdWx0aXBsZSBDb25kaXRpb25zDQoNCkFsdGVybmF0aXZlbHksIHdlIGNhbiB1c2UgYSBzZWNvbmQgbWV0aG9kIHRoYXQgaW52b2x2ZXMgY2hhaW5pbmcgdGhyZWUgY29uZGl0aW9ucyB1c2luZyB0aGUgYHxgIG9wZXJhdG9yLiBXaGlsZSB0aGlzIGFwcHJvYWNoIGlzIG1vcmUgdmVyYm9zZSwgaXQgbWFrZXMgdGhlIGxvZ2ljIHZlcnkgZXhwbGljaXQ6DQoNCmBgYHtyLCBkYXRhLWlzb2xhdGlvbi03LCByZXN1bHRzID0gJ2hpZGUnfQ0KanNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgPT0gMSB8IGZlZWxSdXNoZWQgPT0gMiB8IGZlZWxSdXNoZWQgPT0gMykgfD4gDQogIGhlYWQoKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KanNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgPT0gMSB8IGZlZWxSdXNoZWQgPT0gMiB8IGZlZWxSdXNoZWQgPT0gMykgfD4gDQogIGhlYWQoKSB8Pg0KICBrYmwoKSB8Pg0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8Pg0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KYGBgDQoNCmBgYHtyLCBkYXRhLWlzb2xhdGlvbi0yMCwgcmVzdWx0cyA9ICdoaWRlJ30NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkID09IDQgfCBmZWVsUnVzaGVkID09IDUgfCBmZWVsUnVzaGVkID09IDYpIHw+DQogIGhlYWQoKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KanNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgPT0gNCB8IGZlZWxSdXNoZWQgPT0gNSB8IGZlZWxSdXNoZWQgPT0gNikgfD4NCiAgaGVhZCgpIHw+DQogIGtibCgpIHw+DQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+DQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KVGhpcyBtZXRob2QgdmVyeSBleHBsaWNpdGx5IHRlc3RzIGZvciByb3dzIHdoZXJlIHRoZSBgZmVlbFJ1c2hlZGAgdmFsdWUgaXMgZWl0aGVyIGAxYCwgYDJgLCBvciBgM2AsIG9yIGZvciByb3dzIHdoZXJlIHRoZSB2YWx1ZSBpcyBlaXRoZXIgYDRgLCBgNWAgb3IgYDZgLiANCg0KQWdhaW4sIGxldCdzIGNhbGN1bGF0ZSBob3cgbWFueSByb3dzIHJlbWFpbiBhZnRlciB3ZSBwZXJmb3JtZWQgZmlsdGVyaW5nLg0KDQpgYGB7cn0NCnJ1c2hlZF9yb3dzIDwtIGpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkID09IDEgfCBmZWVsUnVzaGVkID09IDIgfCBmZWVsUnVzaGVkID09IDMpIHw+DQogIG5yb3coKQ0Kbm90X3J1c2hlZF9yb3dzIDwtIGpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkID09IDQgfCBmZWVsUnVzaGVkID09IDUgfCBmZWVsUnVzaGVkID09IDYpIHw+DQogIG5yb3coKQ0KDQpwcmludChwYXN0ZSgiVGhlIG51bWJlciBvZiByb3dzIGluIHJ1c2hlZCBpczoiLCBydXNoZWRfcm93cykpDQpwcmludChwYXN0ZSgiVGhlIG51bWJlciBvZiByb3dzIGluIG5vdCBydXNoZWQgaXM6Iiwgbm90X3J1c2hlZF9yb3dzKSkNCmBgYA0KDQpBcyB3ZSBjYW4gc2VlLCBsb29raW5nIGF0IHRoZSByZW1haW4gcm93cyBhZnRlciBmaWx0ZXJpbmcsIGl0IHByb2R1Y2VzIHRoZSBzYW1lIG91dHB1dCBhcyBwcmV2aW91c2x5Lg0KDQpUaGVyZSBpcyBhbHNvIGEgdGhpcmQsIG1vcmUgZWxlZ2FudCBhcHByb2FjaCB1c2luZyB0aGUgYCVpbiVgIG9wZXJhdG9yLCBidXQgd2Ugd2lsbCBleHBsb3JlIHRoaXMgbWV0aG9kIGluIHRoZSBleGVyY2lzZXMgYXQgdGhlIGVuZCBvZiB0aGlzIHNlc3Npb24uDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBTdGVwIDQ6IFNlbGVjdCBSZWxldmFudCBWYXJpYWJsZXMNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiFbXShpbWFnZXMvMy1BQ1QtMy1TZWxlY3QucG5nKQ0KDQo8aDQgc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPg0KDQpTZWxlY3RpbmcgYW5kIENsZWFuaW5nIFRpbWUtUmVsYXRlZCBWYXJpYWJsZXMNCg0KPC9oND4NCg0KPCEtLSAjIyMgU2VsZWN0aW5nIGFuZCBDbGVhbmluZyBUaW1lLVJlbGF0ZWQgVmFyaWFibGVzIC0tPg0KDQpOZXh0LCB3ZSdsbCBmb2N1cyBvbiB0aGUga2V5IHRpbWUgYWxsb2NhdGlvbiBjb2x1bW5zIHRoYXQgYXJlIG1vc3QgcmVsZXZhbnQgdG8gb3VyIGFuYWx5c2lzOiBgZHVyV29ya2AsIGBkdXJTbGVlcGAsIGFuZCBgZHVyQWxvbmVgLiBUaGVzZSB2YXJpYWJsZXMgcmVwcmVzZW50Og0KDQotICAgYGR1cldvcmtgOiBUaW1lIHNwZW50IG9uIHBhaWQgd29yayBhY3Rpdml0aWVzDQotICAgYGR1clNsZWVwYDogVGltZSBzcGVudCBzbGVlcGluZywgcmVzdGluZywgb3IgcmVsYXhpbmcNCi0gICBgZHVyQWxvbmVgOiBUaW1lIHNwZW50IGFsb25lDQoNCjo6OiBub3RlDQpUaGUgYHNlbGVjdCgpYCBmdW5jdGlvbiBpbiBgZHBseXJgIGlzIHVzZWQgdG8gY2hvb3NlIHNwZWNpZmljIGNvbHVtbnMgZnJvbSBhIGRhdGFzZXQuDQo6OjoNCg0KV2UnbGwgc2VsZWN0IG9ubHkgdGhlc2UgdGhyZWUgY29sdW1ucyBmcm9tIG91ciBydXNoZWQgYW5kIG5vdF9ydXNoZWQgZGF0YXNldHMgdXNpbmcgYHNlbGVjdCgpYC4NCg0KYGBge3J9DQpydXNoZWRfdGltZSA8LSBqc19kYXRhIHw+IA0KICBmaWx0ZXIoZmVlbFJ1c2hlZCA8PSAzKSB8Pg0KICBzZWxlY3QoZHVyV29yaywgZHVyU2xlZXAsIGR1ckFsb25lKSANCg0Kbm90X3J1c2hlZF90aW1lIDwtIGpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkID4gMykgfD4gDQogIHNlbGVjdChkdXJXb3JrLCBkdXJTbGVlcCwgZHVyQWxvbmUpDQpgYGANCg0KOjo6IHdhbGt0aHJvdWdoDQpJbiB0aGlzIGV4YW1wbGUsIHdlIHVzZSB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbiB0byBleHRyYWN0IG9ubHkgdGhlIHJlbGV2YW50IGNvbHVtbnMgZnJvbSB0aGUgZGF0YXNldCByZXN1bHRpbmcgZnJvbSBmaWx0ZXJpbmcgcm93cyBvZiB0aG9zZSB3aG8gZmVlbCBydXNoZWQgKGkuZS4sIGBmZWVsUnVzaGVkIDw9M2ApIGFuZCB0aG9zZSB3aG8gZG9uJ3QgZmVlbCBydXNoZWQgKGkuZS4sIGBmZWVsUnVzaGVkID4gM2ApLg0KDQotICAgYHNlbGVjdChkdXJXb3JrLCBkdXJTbGVlcCwgZHVyQWxvbmUpYCB0ZWxscyBSIHRvIGtlZXAgb25seSB0aGVzZSB0aHJlZSBjb2x1bW5zOg0KICAgIC0gICBgZHVyV29ya2A6IGR1cmF0aW9uIG9mIHBhaWQgd29yaw0KICAgIC0gICBgZHVyU2xlZXBgOiBkdXJhdGlvbiBvZiBzbGVlcCBhbmQgcmVzdA0KICAgIC0gICBgZHVyQWxvbmVgOiBkdXJhdGlvbiBvZiB0aW1lIHNwZW50IGFsb25lDQotICAgVGhlIGB8PmAgKHBpcGUgb3BlcmF0b3IpIHBhc3NlcyB0aGUgZGF0YSBmcmFtZXMgb24gdGhlIGxlZnQgaW50byB0aGUgYHNlbGVjdCgpYCBmdW5jdGlvbi4NCg0KVGhpcyBzdGVwIGhlbHBzIHJlZHVjZSB0aGUgZGF0YXNldCB0byBvbmx5IHRoZSB2YXJpYWJsZXMgd2UgY2FyZSBhYm91dCBmb3IgY29tcGFyaW5nIGhvdyBydXNoZWQgYW5kIG5vdC1ydXNoZWQgaW5kaXZpZHVhbHMgYWxsb2NhdGUgdGhlaXIgdGltZS4NCjo6Og0KDQpJbiB0aGUgcHJldmlvdXMgY29kZSwgd2UgYXNzaWduZWQgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lcyB0byB0aGUgdmFyaWFibGVzIGBub3RfcnVzaGVkX3RpbWVgIGFuZCBgcnVzaGVkX3RpbWVgLiBUaGVzZSB2YXJpYWJsZXMgd2lsbCBiZSB1c2VmdWwgd2hlbiB3ZSBjb21wYXJlIHRpbWUgdXNhZ2UgYmV0d2VlbiB0d28gZ3JvdXBzLg0KDQpMZXQncyBleGFtaW5lIHRoZSBmaXJzdCBmZXcgcm93cyBvZiBgcnVzaGVkX3RpbWVgIGFuZCBgbm90X3J1c2hlZF90aW1lYC4NCg0KYGBge3IsIGRhdGEtaXNvbGF0aW9uLTkxLCByZXN1bHRzID0gJ2hpZGUnfQ0KcnVzaGVkX3RpbWUgfD4gDQogIGhlYWQoKQ0KDQpub3RfcnVzaGVkX3RpbWUgfD4gDQogIGhlYWQoKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KaGVhZChydXNoZWRfdGltZSkgfD4NCiAga2JsKCkgfD4NCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4NCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCg0KaGVhZChub3RfcnVzaGVkX3RpbWUpIHw+DQogIGtibCgpIHw+DQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+DQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KQXMgd2UgY2FuIHNlZSwgb3VyIGRhdGEgZnJhbWUgcmVzdWx0aW5nIGZyb20gdXNpbmcgYHNlbGVjdCgpYCBvbmx5IGNvbnRhaW4gMyBjb2x1bW5zOiBgZHVyU2xlZXBgLCBgZHVyQWxvbmVgLCBhbmQgYGR1cldvcmtgDQoNCjo6OiBub3RlDQoqKkhlbHBlciBGdW5jdGlvbnMgZm9yIGBzZWxlY3QoKWAqKg0KDQpTdXBwb3NlIHdlIHdhbnQgdG8gcHVsbCBvdXQgZXZlcnkgY29sdW1uIGluIGBqc19kYXRhYCB3aG9zZSBuYW1lIHJlZmVycyB0byAiZHVyYXRpb24iLS0tZm9yIGV4YW1wbGUsIGBkdXJXb3JrYCwgYGR1ckRyaXZpbmdgLCBgZHVyU2Nob29sU2l0ZWAsIGFuZCBzbyBvbi4gV2UgY291bGQgbWFudWFsbHkgbGlzdCB0aGVtIGluIGBzZWxlY3QoKWAsIGJ1dCB0aGF0IHF1aWNrbHkgYmVjb21lcyB0ZWRpb3VzIGFuZCBlcnJvci1wcm9uZSBhcyBvdXIgZGF0YSBncm93cy4NCg0KYGRwbHlyYCBwcm92aWRlcyBhIGZhbWlseSBvZiAqKmhlbHBlcioqIGZ1bmN0aW9ucyBmb3IgZXhhY3RseSB0aGlzIGtpbmQgb2YgdGFzay4gSW4gdGhpcyBjYXNlLCBgc3RhcnRzX3dpdGgoImR1ciIpYCB3aWxsIG1hdGNoIGV2ZXJ5IHZhcmlhYmxlIHdob3NlIG5hbWUgYmVnaW5zIHdpdGggImR1ciIuIFdlIGNhbiBwYXNzIHRoYXQgaGVscGVyIGRpcmVjdGx5IHRvIGBzZWxlY3QoKWA6DQoNCmBgYHtyLCBkYXRhLWlzb2xhdGlvbi05LCByZXN1bHRzID0gJ2hpZGUnfQ0KanNfZGF0YSB8PiANCiAgc2VsZWN0KHN0YXJ0c193aXRoKCJkdXIiKSkgfD4NCiAgaGVhZCgpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpqc19kYXRhIHw+IA0KICBzZWxlY3Qoc3RhcnRzX3dpdGgoImR1ciIpKSB8Pg0KICBoZWFkKCkgfD4NCiAga2JsKCkgfD4NCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4NCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCmBgYA0KDQpUaGVyZSBhcmUgbWFueSBvdGhlciBoZWxwZXJzLCBzdWNoIGFzIGBlbmRzX3dpdGgoKWAsIGBjb250YWlucygpYCwgYG1hdGNoZXMoKWAsIGFuZCBtb3JlLCB3aGljaCBsZXQgdXMgYnVpbGQgY2xlYW4sIGZsZXhpYmxlIGNvZGUgd2l0aG91dCBoYXZpbmcgdG8gc3BlbGwgb3V0IGV2ZXJ5IHZhcmlhYmxlIG5hbWUuIFdlIGNhbiBjaGVjayB0aGUgc2VsZWN0IFtyZWZlcmVuY2VdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc2VsZWN0Lmh0bWwpIGluIHRpZHl2ZXJzZSBmb3IgbW9yZSBpbmZvcm1hdGlvbi4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgU3RlcCA1OiBDb21wYXJlIFRpbWUgVXNlIEJldHdlZW4gR3JvdXBzDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQo8aDQgc3R5bGU9InRleHQtYWxpZ246bGVmdDsiPg0KDQpDb21wYXJpbmcgVGltZSBVc2FnZSBCZXR3ZWVuIEdyb3Vwcw0KDQo8L2g0Pg0KDQo8IS0tICMjIyBSZXN1bHRzOiBDb21wYXJpbmcgVGltZSBVc2FnZSBCZXR3ZWVuIEdyb3VwcyAtLT4NCg0KTm93IHRoYXQgd2UgaGF2ZSBvdXIgY2xlYW5lZCBkYXRhc2V0cyBmb3IgYm90aCBydXNoZWQgYW5kIG5vdCBydXNoZWQgZ3JvdXBzLCB3ZSBjYW4gYW5hbHl6ZSBob3cgdGhlc2UgZ3JvdXBzIGRpZmZlciBpbiB0aGVpciB0aW1lIGFsbG9jYXRpb24gcGF0dGVybnMuIFdlJ2xsIGNhbGN1bGF0ZSBhbmQgY29tcGFyZSB0aGUgbWVhbiBkdXJhdGlvbnMgZm9yIHNsZWVwLCB3b3JrLCBhbmQgYWxvbmUgdGltZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzLg0KDQpMZXQncyBicmVhayB0aGlzIGRvd24gaW50byBzdGVwczogMS4gRmlyc3QsIHdlJ2xsIGNhbGN1bGF0ZSB0aGUgbWVhbiB2YWx1ZXMgZm9yIGVhY2ggdGltZS11c2UgdmFyaWFibGUgd2l0aGluIGVhY2ggZ3JvdXAuIDIuIFRoZW4sIHdlJ2xsIGNvbXB1dGUgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlc2UgbWVhbnMgdG8gdW5kZXJzdGFuZCB0aGUgbWFnbml0dWRlIG9mIHZhcmlhdGlvbi4NCg0KV2UgY2FuIGNyZWF0ZSBhIHN1bW1hcnkgdGFibGUgdGhhdCBjb250YWlucyB0aGUgbWVhbiB2YWx1ZXMgb2YgYGR1clNsZWVwYCwgYGR1ckFsb25lYCwgYW5kIGBkdXJXb3JrYCBmb3IgZWFjaCBvZiB0aGUgcnVzaGVkX3RpbWUgYW5kIG5vdF9ydXNoZWRfdGltZSBkYXRhZnJhbWVzOg0KDQpgYGB7cn0NCm1lYW5fcnVzaGVkIDwtIHJ1c2hlZF90aW1lIHw+IA0KICBzdW1tYXJpc2UoDQogICAgZHVyU2xlZXAgPSBtZWFuKGR1clNsZWVwLCBuYS5ybSA9IFRSVUUpLA0KICAgIGR1ckFsb25lID0gbWVhbihkdXJBbG9uZSwgbmEucm0gPSBUUlVFKSwNCiAgICBkdXJXb3JrID0gbWVhbihkdXJXb3JrLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KbWVhbl9ub3RfcnVzaGVkIDwtIG5vdF9ydXNoZWRfdGltZSB8PiANCiAgc3VtbWFyaXNlKA0KICAgIGR1clNsZWVwID0gbWVhbihkdXJTbGVlcCwgbmEucm0gPSBUUlVFKSwNCiAgICBkdXJBbG9uZSA9IG1lYW4oZHVyQWxvbmUsIG5hLnJtID0gVFJVRSksDQogICAgZHVyV29yayA9IG1lYW4oZHVyV29yaywgbmEucm0gPSBUUlVFKQ0KICApDQpgYGANCg0KOjo6IHdhbGt0aHJvdWdoDQpIb3cgaXQgd29ya3M6DQoNCi0gICBgc3VtbWFyaXNlKClgIGNvbGxhcHNlcyBlYWNoIGRhdGEgZnJhbWUgZG93biB0byBhIHNpbmdsZSByb3cuDQotICAgSW5zaWRlLCB3ZSBleHBsaWNpdGx5IG5hbWUgZWFjaCBuZXcgY29sdW1uIChgZHVyU2xlZXBgLCBgZHVyQWxvbmVgLCBgZHVyV29ya2ApIGFuZCBhc3NpZ24gaXQgYG1lYW4ob2xkX2NvbHVtbiwgbmEucm0gPSBUUlVFKWAuDQotICAgYG5hLnJtID0gVFJVRWAgbWFrZXMgc3VyZSBtaXNzaW5nIHZhbHVlcyBkb24ndCB0aHJvdyBvZmYgb3VyIGF2ZXJhZ2VzLg0KOjo6DQoNCkhvd2V2ZXIsIHdlIGNhbiBzZWUgdGhlcmUncyBhIGxpdHRsZSBiaXQgb2YgcmVwZXRpdGlvbiBpbiB0aGUgY29kZSBhYm92ZS4gV2UgY2FuIHdyaXRlIG11Y2ggY2xlYW5lciBjb2RlIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiBmb3IgZXZlcnkgY29sdW1uIGF0IG9uY2UuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIG1lYW4gdmFsdWVzIGZvciBlYWNoIHZhcmlhYmxlIHVzaW5nIHN1bW1hcmlzZSBhbmQgYWNyb3NzDQptZWFuX3J1c2hlZCA8LSBydXNoZWRfdGltZSB8PiANCiAgc3VtbWFyaXNlKGFjcm9zcyhldmVyeXRoaW5nKCksIH4gbWVhbiguICwgbmEucm0gPSBUUlVFKSkpDQoNCm1lYW5fbm90X3J1c2hlZCA8LSBub3RfcnVzaGVkX3RpbWUgfD4gDQogIHN1bW1hcmlzZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+IG1lYW4oLiAsIG5hLnJtID0gVFJVRSkpKQ0KDQpgYGANCg0KOjo6IHdhbGt0aHJvdWdoDQpJbiB0aGlzIGNvZGU6DQoNCi0gICBXZSB1c2UgYHN1bW1hcmlzZSgpYCB3aXRoIGBhY3Jvc3MoKWAgdG8gY2FsY3VsYXRlIG1lYW5zIGZvciBhbGwgY29sdW1ucyBhdCBvbmNlDQotICAgVGhlIGBuYS5ybSA9IFRSVUVgIGFyZ3VtZW50IGVuc3VyZXMgd2UgZXhjbHVkZSBtaXNzaW5nIHZhbHVlcyBmcm9tIG91ciBjYWxjdWxhdGlvbnMNCjo6Og0KDQpXZSBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgaW4gbWVhbnMgYmV0d2VlbiB0aGVzZSB0d28gZ3JvdXBzLg0KDQpgYGB7cn0NCmRpZmZfbWVhbnMgPC0gbWVhbl9ydXNoZWQgLSBtZWFuX25vdF9ydXNoZWQNCmBgYA0KDQpUaGUgc3VidHJhY3Rpb24gKGBtZWFuX3J1c2hlZCAtIG1lYW5fbm90X3J1c2hlZGApIGdpdmVzIGEgbmV3IG9uZeKAkHJvdyB0aWJibGUgc2hvd2luZyBob3cgbXVjaCBtb3JlIChvciBsZXNzKSB0aW1lICJydXNoZWQiIGluZGl2aWR1YWxzIHNwZW5kIG9uIGVhY2ggYWN0aXZpdHkgY29tcGFyZWQgdG8gIm5vdCBydXNoZWQiIGluZGl2aWR1YWxzLg0KDQpOb3csIGxldCdzIHNlZSB3aGF0IHdlIGdldCB1cCB0byB0aGlzIHBvaW50Lg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgUHJpbnQgdGhlIHJlc3VsdHMNCnByaW50KCJNZWFuIHZhbHVlcyBmb3IgcmVzcG9uZGVudHMgd2hvIGZlZWwgcnVzaGVkOiIpDQptZWFuX3J1c2hlZCB8Pg0KICBrYmwoKSB8Pg0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8Pg0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KDQpwcmludCgiTWVhbiB2YWx1ZXMgZm9yIHJlc3BvbmRlbnRzIHdobyBkbyBub3QgZmVlbCBydXNoZWQ6IikNCm1lYW5fbm90X3J1c2hlZCB8Pg0KICBrYmwoKSB8Pg0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8Pg0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KDQpwcmludCgiRGlmZmVyZW5jZSBiZXR3ZWVuIHJ1c2hlZCBhbmQgbm90IHJ1c2hlZCAocnVzaGVkIC0gbm90IHJ1c2hlZCk6IikNCmRpZmZfbWVhbnMgfD4NCiAga2JsKCkgfD4NCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4NCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCmBgYA0KDQo6Ojogbm90ZQ0KUG9zaXRpdmUgdmFsdWVzIGluIHRoZSBkaWZmZXJlbmNlIGNhbGN1bGF0aW9uIGluZGljYXRlIHRoYXQgcnVzaGVkIGluZGl2aWR1YWxzIHNwZW5kIG1vcmUgdGltZSBvbiB0aGF0IGFjdGl2aXR5LCB3aGlsZSBuZWdhdGl2ZSB2YWx1ZXMgaW5kaWNhdGUgdGhleSBzcGVuZCBsZXNzIHRpbWUgY29tcGFyZWQgdG8gdGhvc2Ugd2hvIGRvbid0IGZlZWwgcnVzaGVkLg0KOjo6DQoNClRoZXNlIHJlc3VsdHMgcHJvdmlkZSBpbnRlcmVzdGluZyBpbnNpZ2h0cyBpbnRvIGhvdyBmZWVsaW5nIHJ1c2hlZCByZWxhdGVzIHRvIHRpbWUgYWxsb2NhdGlvbiBwYXR0ZXJucy4gRm9yIGluc3RhbmNlLCB3ZSBjYW4gb2JzZXJ2ZSB3aGV0aGVyIHBlb3BsZSB3aG8gZmVlbCBydXNoZWQgYWN0dWFsbHkgc3BlbmQgbW9yZSB0aW1lIHdvcmtpbmcgb3IgbGVzcyB0aW1lIHNsZWVwaW5nIHRoYW4gdGhvc2Ugd2hvIGRvbid0IGZlZWwgcnVzaGVkLCB3aGljaCBtaWdodCBoZWxwIGV4cGxhaW4gdGhlaXIgcGVyY2VwdGlvbiBvZiB0aW1lIHByZXNzdXJlLg0KDQojIyBCZWZvcmUgdGhlIE5leHQgRXhlcmNpc2U6IFJlY29kZSBWYXJpYWJsZXMgYW5kIFNhdmUgRGF0YXNldA0KDQpJbiB0aGUgbmV4dCBleGVyY2lzZSwgd2Ugd2lsbCBjb21wYXJlIHRoZSB0aW1lIHByZXNzdXJlIGZlbHQgYnkgcmVzcG9uZGVudHMgYmFzZWQgb24gd2hldGhlciB0aGV5IGxpdmUgaW4gYW4gKip1cmJhbioqIG9yICoqcnVyYWwqKiBhcmVhLCBhcyBjb2RlZCBpbiB0aGUgYHBvcENlbnRlcmAgY29sdW1uOg0KDQpgYGB7cn0NCmpzX2RhdGEgfD4NCiAgY291bnQocG9wQ2VudGVyKQ0KYGBgDQoNCkFjY29yZGluZyB0byB0aGUgZGF0YSBkaWN0aW9uYXJ5LCB0aGVzZSBudW1lcmljIGNvZGVzIG1lYW46DQoNCnwgdmFsdWUgfCBsYWJlbCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCAxICAgICB8IExhcmdlciB1cmJhbiBwb3B1bGF0aW9uIGNlbnRyZXMgKENNQS9DQSkgICAgICAgICAgICAgIHwNCnwgMiAgICAgfCBSdXJhbCBhcmVhcyBhbmQgc21hbGwgcG9wdWxhdGlvbiBjZW50cmVzIChub24gQ01BL0NBKSB8DQp8IDMgICAgIHwgUHJpbmNlIEVkd2FyZCBJc2xhbmQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KDQpXZSBjYW4gdXNlICoqZHBseXIqKidzIGBtdXRhdGUoKWAgYWxvbmcgd2l0aCBgaWZfZWxzZSgpYCB0byB0dXJuIHRob3NlIG51bWJlcnMgaW50byBkZXNjcmlwdGl2ZSBsYWJlbHM6DQoNCmBgYHtyfQ0KanNfZGF0YSA8LSBqc19kYXRhIHw+IG11dGF0ZSgNCiAgcG9wQ2VudGVyPWlmX2Vsc2UocG9wQ2VudGVyPT0xLCd1cmJhbicsDQogICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UocG9wQ2VudGVyPT0yLCdydXJhbCcsJ1BFSScpKQ0KKQ0KYGBgDQoNCjo6OiB3YWxrdGhyb3VnaA0KSG93IGl0IHdvcmtzOg0KDQotICAgYG11dGF0ZSgpYDogYWRkcyBvciBtb2RpZmllcyBjb2x1bW5zLg0KLSAgIGBpZl9lbHNlKGNvbmRpdGlvbiwgdHJ1ZSwgZmFsc2UpYDogYSB2ZWN0b3JpemVkLCB0eXBlLXNhZmUgY29uZGl0aW9uYWwuDQotICAgSGVyZSwgd2UgbmVzdCB0d28gYGlmX2Vsc2UoKWAgY2FsbHMgc28gdGhhdDoNCiAgICAtICAgYHBvcENlbnRlciA9PSAxYCDihpIgInVyYmFuIg0KICAgIC0gICBgcG9wQ2VudGVyID09IDJgIOKGkiAicnVyYWwiDQogICAgLSAgIG90aGVyd2lzZSAoY29kZSBgM2ApIOKGkiAiUEVJIg0KOjo6DQoNCk5leHQsIHdlJ2xsIGNyZWF0ZSBhIG5ldyBmbGFnIGNhbGxlZCBgaXNGZWVsUnVzaGVkYCB0byBtYXJrIHdoZXRoZXIgZWFjaCByZXNwb25kZW50IGZlZWxzIHJ1c2hlZCBvciBub3QuIFRoaXMgd2lsbCBiZSB1c2VmdWwgZm9yIG91ciB1cGNvbWluZyBkYXRhLXZpc3VhbGl6YXRpb24gdHV0b3JpYWw6DQoNCmBgYHtyfQ0KanNfZGF0YSA8LSBqc19kYXRhIHw+IG11dGF0ZSgNCiAgaXNGZWVsUnVzaGVkPWlmX2Vsc2UoZmVlbFJ1c2hlZCA8PSAzLDEsMCkNCikNCmBgYA0KDQo6Ojogd2Fsa3Rocm91Z2gNCkluIHRoaXMgY29kZToNCg0KLSAgIFdlIGFzc2lnbiB0aGUgcmVzdWx0IGJhY2sgaW50byBganNfZGF0YWAgc28gdGhlIG5ldyBjb2x1bW4gaXMgc2F2ZWQuDQotICAgVGhlIGBpZl9lbHNlKClgIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgYmluYXJ5IHZhcmlhYmxlOg0KICAgIC0gICBgaXNGZWVsUnVzaGVkID0gMWAgaWYgYGZlZWxSdXNoZWRgIGlzIGAxYCwgYDJgLCBvciBgM2AgKGkuZS4gdGhlIHJlc3BvbmRlbnQgZmVlbHMgcnVzaGVkKQ0KICAgIC0gICBgaXNGZWVsUnVzaGVkID0gMGAgb3RoZXJ3aXNlIChpLmUuIG5vdCBydXNoZWQpDQo6OjoNCg0KRmluYWxseSwgc2F2ZSB0aGUgcmVjb2RlZCBkYXRhc2V0IHNvIGl0J3MgcmVhZHkgZm9yIHRoZSBuZXh0IHN0ZXBzOg0KDQpgYGB7cn0NCnNhdmUoanNfZGF0YSwgZmlsZSA9ICJkYXRhL3RpbWVfdXNlX2RheTNfMi5SRGF0YSIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIEV4ZXJjaXNlczogVGltZSBmb3IgUHJhY3RpY2UhDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgT3B0aW9uYWwgRXhlcmNpc2UgMTogVXNpbmcgYCVpbiVgIE9wZXJhdG9yDQoNCkluIHRoZSBwcmV2aW91cyBzZWN0aW9ucywgd2UgbGVhcm5lZCB0aGF0IHdlIGNhbiBmaWx0ZXIgcmVzcG9uZGVudHMgd2hvIGZlZWwgcnVzaGVkIG9yIG5vdCBieSBlaXRoZXIgdXNpbmcgYSByYW5nZSBjb21wYXJpc29uIChlLmcuLCBgZmVlbFJ1c2hlZCA8PSAzYCkgb3IgYnkgY2hhaW5pbmcgbXVsdGlwbGUgY29uZGl0aW9ucyB3aXRoIGxvZ2ljYWwgb3BlcmF0b3JzIChlLmcuLCBgZmVlbFJ1c2hlZCA9PSAxIHwgZmVlbFJ1c2hlZCA9PSAyIHwgZmVlbFJ1c2hlZCA9PSAzYCkuDQoNCkhvd2V2ZXIsIHRoZXJlJ3MgYWxzbyBhIHRoaXJkLCBtb3JlIGVsZWdhbnQgYXBwcm9hY2g6IHVzaW5nIHRoZSBgJWluJWAgb3BlcmF0b3IuIFRoaXMgbWV0aG9kIGlzIGVzcGVjaWFsbHkgaGVscGZ1bCB3aGVuIHdlIHdhbnQgdG8gZmlsdGVyIGEgZGF0YXNldCBiYXNlZCBvbiBtdWx0aXBsZSBzcGVjaWZpYyB2YWx1ZXMgb2YgYSB2YXJpYWJsZSwgbWFraW5nIG91ciBjb2RlIHNob3J0ZXIgYW5kIGVhc2llciB0byByZWFkLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KOjo6IHF1ZXN0aW9uDQoqKkV4YW1wbGUgU2NlbmFyaW8qKg0KDQpSZW1lbWJlciB0aGUgcHJldmlvdXMgcXVlc3Rpb24gd2hlcmUgd2UncmUgYW5hbHl6aW5nIHN1cnZleSBkYXRhIG9uIGhvdyBmcmVxdWVudGx5IHBlb3BsZSBmZWVsIHJ1c2hlZC4gVGhlIGBmZWVsUnVzaGVkYCB2YXJpYWJsZSBpbmNsdWRlcyB2YWx1ZXMgZnJvbSAxIChkYWlseSkgdG8gNiAobmV2ZXIpLiBXZSB3YW50IHRvIGdyb3VwIHJlc3BvbmRlbnRzIGludG8gdHdvIGNhdGVnb3JpZXM6DQoNCi0gICAqKlJ1c2hlZCoqOiB0aG9zZSB3aG8gZmVlbCBydXNoZWQgYXQgbGVhc3Qgb25jZSBhIHdlZWsgKGAxYCwgYDJgLCBgM2ApDQotICAgKipOb3QgUnVzaGVkKio6IHRob3NlIHdobyBmZWVsIHJ1c2hlZCBsZXNzIG9mdGVuIChgNGAsIGA1YCwgYDZgKQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KKipUYXNrIEluc3RydWN0aW9ucyoqDQoNCjEuICBEZWZpbmUgdGhlIHNldHMgb2YgdmFsdWVzIHRoYXQgcmVwcmVzZW50IGVhY2ggZ3JvdXAuDQoyLiAgVXNlIHRoZSBgJWluJWAgb3BlcmF0b3IgaW5zaWRlIGBmaWx0ZXIoKWAgdG8gY3JlYXRlIHN1YnNldHMuDQozLiAgRGlzcGxheSB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgZWFjaCBzdWJzZXQgdG8gdmVyaWZ5IGNvcnJlY3RuZXNzLg0KNC4gIENvdW50IHRoZSBudW1iZXIgb2Ygcm93cyBpbiBlYWNoIGdyb3VwIHRvIGNvbXBhcmUgc2l6ZXMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoqKlN0ZXBzIDEgJiAyOiBEZWZpbmUgdGhlIHNldHMgYW5kIHVzZSBgJWluJWAgaW5zaWRlIGBmaWx0ZXIoKWAqKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30NCiMgRGVmaW5lIGdyb3VwIGxldmVscw0KcnVzaGVkX2xldmVscyA8LSBjKDEsIDIsIDMpDQpub3RfcnVzaGVkX2xldmVscyA8LSBjKDQsIDUsIDYpDQoNCiMgRmlsdGVyIHVzaW5nICVpbiUNCmpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkICVpbiUgcnVzaGVkX2xldmVscykNCg0KanNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgJWluJSBub3RfcnVzaGVkX2xldmVscykNCmBgYA0KDQoqKlN0ZXAgM2E6IFZpZXcgdGhlIGZpcnN0IGZldyByb3dzIG9mIGBydXNoZWRfbGV2ZWxzYCoqDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCByZXN1bHRzID0gJ2hpZGUnfQ0KanNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgJWluJSBydXNoZWRfbGV2ZWxzKSB8PiANCiAgaGVhZCgpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpqc19kYXRhIHw+IA0KICBmaWx0ZXIoZmVlbFJ1c2hlZCAlaW4lIHJ1c2hlZF9sZXZlbHMpIHw+IA0KICBoZWFkKCkgfD4gDQogIGtibCgpIHw+IA0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PiANCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCmBgYA0KDQoqKlN0ZXAgM2I6IFZpZXcgdGhlIGZpcnN0IGZldyByb3dzIG9mIGBub3RfcnVzaGVkX2xldmVsc2AqKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkICVpbiUgbm90X3J1c2hlZF9sZXZlbHMpIHw+IA0KICBoZWFkKCkNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihmZWVsUnVzaGVkICVpbiUgbm90X3J1c2hlZF9sZXZlbHMpIHw+DQogIGhlYWQoKSB8Pg0KICBrYmwoKSB8PiANCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4gDQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KKipTdGVwIDQ6IENvdW50IHJvd3MqKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJ30NCg0KcnVzaGVkX3Jvd3MgPC0ganNfZGF0YSB8PiANCiAgZmlsdGVyKGZlZWxSdXNoZWQgJWluJSBydXNoZWRfbGV2ZWxzKSB8PiANCiAgbnJvdygpDQoNCm5vdF9ydXNoZWRfcm93cyA8LSBqc19kYXRhIHw+IA0KICBmaWx0ZXIoZmVlbFJ1c2hlZCAlaW4lIG5vdF9ydXNoZWRfbGV2ZWxzKSB8PiANCiAgbnJvdygpDQoNCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHJvd3MgaW4gcnVzaGVkIGlzOiIsIHJ1c2hlZF9yb3dzKSkNCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIHJvd3MgaW4gbm90IHJ1c2hlZCBpczoiLCBub3RfcnVzaGVkX3Jvd3MpKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoqKlN1bW1hcnkqKg0KDQpUaGlzIGV4YW1wbGUgc2hvd3MgaG93IHRoZSBgJWluJWAgb3BlcmF0b3Igc2ltcGxpZmllcyBmaWx0ZXJpbmcgd2hlbiB3b3JraW5nIHdpdGggY2F0ZWdvcmljYWwgdmFyaWFibGVzLiBJdCBhdm9pZHMgdGhlIG5lZWQgdG8gd3JpdGUgbXVsdGlwbGUgYD09YCBhbmQgYHxgIGNvbmRpdGlvbnMsIG1ha2luZyBvdXIgY29kZSBjbGVhbmVyIGFuZCBlYXNpZXIgdG8gcmVhZC4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIE9wdGlvbmFsIEV4ZXJjaXNlIDI6IFVyYmFuIHZzIFJ1cmFsIFRpbWUgUHJlc3N1cmUgQW5hbHlzaXMNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB3YWxrdGhyb3VnaA0KTGV0J3MgdXNlIG91ciBuZXcgZmlsdGVyaW5nIGFuZCBzZWxlY3Rpbmcgc2tpbGxzIHRvIGV4cGxvcmUgaG93IHBlb3BsZSBpbiB1cmJhbiBhbmQgcnVyYWwgYXJlYXMgZGlmZmVyIGluIHRlcm1zIG9mIHRpbWUgcHJlc3N1cmUsIGJhc2VkIG9uIHRoZWlyIHJlcG9ydGVkIGV4dHJhIHRpbWUuDQoNCioqVGFzayBJbnN0cnVjdGlvbnMqKg0KDQoxLiAgRXhwbG9yZSB0aGUgYHBvcENlbnRlcmAgYW5kIGBleHRyYVRpbWVgIGNvbHVtbnMgdG8gdW5kZXJzdGFuZCB0aGUgdmFyaWFibGUgdHlwZXMgYW5kIHBvc3NpYmxlIHZhbHVlcy4NCjIuICBGaWx0ZXIgdGhlIGRhdGFzZXQgaW50byB0d28gZ3JvdXBzOiBgdXJiYW5gIGFuZCBgcnVyYWxgLg0KMy4gIFNlbGVjdCB0aGUgcmVsZXZhbnQgdmFyaWFibGVzOiBgcG9wQ2VudGVyYCwgYGV4dHJhVGltZWAsIGBkdXJXb3JrYCwgYW5kIGBkdXJTbGVlcGAuDQo0LiAgQ2FsY3VsYXRlIGFuZCBjb21wYXJlIHRoZSBhdmVyYWdlIGBleHRyYVRpbWVgIGJldHdlZW4gdGhlIHR3byBncm91cHMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQoqKlN0ZXAgMTogRXhwbG9yZSB0aGUgRGF0YSoqDQoNCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30NCmpzX2RhdGEgfD4gDQogIGRpc3RpbmN0KHBvcENlbnRlcikNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmpzX2RhdGEgfD4gDQogIGRpc3RpbmN0KHBvcENlbnRlcikgfD4NCiAga2JsKCkgfD4gDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIpIHw+IA0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KYGBgDQoNCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30NCmpzX2RhdGEgfD4gDQogIGNvdW50KGV4dHJhVGltZSkgfD4gDQogIGFycmFuZ2UoZGVzYyhuKSkgfD4gDQogIGhlYWQoKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KanNfZGF0YSB8PiANCiAgY291bnQoZXh0cmFUaW1lKSB8PiANCiAgYXJyYW5nZShkZXNjKG4pKSB8PiANCiAgaGVhZCgpIHw+DQogIGtibCgpIHw+IA0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PiANCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KKipTdGVwIDI6IEZpbHRlciBVcmJhbiBhbmQgUnVyYWwgUG9wdWxhdGlvbnMqKg0KDQpUbyBmaWx0ZXIgdGhlIHVyYmFuIHBvcHVsYXRpb24sIHdlIHVzZSB0aGUgYGZpbHRlcigpYCBmdW5jdGlvbiB0byBrZWVwIG9ubHkgcm93cyB3aGVyZSBgcG9wQ2VudGVyYCBpcyBlcXVhbCB0byBgInVyYmFuImAuIFRoZW4sIHdlIHVzZSBgaGVhZCgpYCB0byBkaXNwbGF5IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgcmVzdWx0aW5nIGRhdGFmcmFtZToNCg0KYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCByZXN1bHRzID0gJ2hpZGUnfQ0KanNfZGF0YSB8PiANCiAgZmlsdGVyKHBvcENlbnRlciA9PSAndXJiYW4nKSB8Pg0KICBoZWFkKCkNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihwb3BDZW50ZXIgPT0gJ3VyYmFuJykgfD4NCiAgaGVhZCgpIHw+IA0KICBrYmwoKSB8PiANCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4gDQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KU2ltaWxhcmx5LCB3ZSBmaWx0ZXIgdGhlIHJ1cmFsIHBvcHVsYXRpb24sIHRoZW4gcHJldmlldyB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIHJlc3VsdGluZyBkYXRhZnJhbWU6DQoNCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30NCmpzX2RhdGEgfD4gDQogIGZpbHRlcihwb3BDZW50ZXIgPT0gJ3J1cmFsJykgfD4NCiAgaGVhZCgpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpqc19kYXRhIHw+IA0KICBmaWx0ZXIocG9wQ2VudGVyID09ICdydXJhbCcpIHw+DQogIGhlYWQoKSB8Pg0KICBrYmwoKSB8PiANCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4gDQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCioqU3RlcCAzOiBTZWxlY3QgUmVsZXZhbnQgVmFyaWFibGVzKioNCg0KV2Ugc2VsZWN0IHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOg0KDQotICAgYHBvcENlbnRlcmA6IHRvIGluZGljYXRlIGdyb3VwIGlkZW50aXR5ICh1cmJhbiB2cy4gcnVyYWwpDQotICAgYGV4dHJhVGltZWA6IHRvIG1lYXN1cmUgcGVyY2VpdmVkIHRpbWUgYXZhaWxhYmlsaXR5DQotICAgYGR1cldvcmtgOiB0byBleGFtaW5lIHdvcmsgZHVyYXRpb24NCi0gICBgZHVyU2xlZXBgOiB0byBldmFsdWF0ZSBzbGVlcCBwYXR0ZXJucw0KDQpXZSBzdGFydCBieSBzZWxlY3RpbmcgdGhlIHJlbGV2YW50IGNvbHVtbnMgZnJvbSB0aGUgZmlsdGVyZWQgdXJiYW4gZGF0YWZyYW1lOg0KDQpgYGB7ciwgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZScsIHJlc3VsdHMgPSAnaGlkZSd9DQp1cmJhbl9zZWxlY3RlZCA8LSBqc19kYXRhIHw+IA0KICBmaWx0ZXIocG9wQ2VudGVyID09ICd1cmJhbicpIHw+IA0KICBzZWxlY3QocG9wQ2VudGVyLCBleHRyYVRpbWUsIGR1cldvcmssIGR1clNsZWVwKQ0KDQp1cmJhbl9zZWxlY3RlZCB8PiANCiAgaGVhZCgpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpoZWFkKHVyYmFuX3NlbGVjdGVkKSB8Pg0KICBrYmwoKSB8PiANCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIikgfD4gDQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KU2ltaWxhcmx5LCB3ZSBzZWxlY3QgY29sdW1ucyBmcm9tIHRoZSBmaWx0ZXJlZCBydXJhbCBkYXRhZnJhbWU6DQoNCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30NCnJ1cmFsX3NlbGVjdGVkIDwtIGpzX2RhdGEgfD4gDQogIGZpbHRlcihwb3BDZW50ZXIgPT0gJ3J1cmFsJykgfD4gDQogIHNlbGVjdChwb3BDZW50ZXIsIGV4dHJhVGltZSwgZHVyV29yaywgZHVyU2xlZXApIA0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KaGVhZChydXJhbF9zZWxlY3RlZCkgfD4gDQogIGtibCgpIHw+IA0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiKSB8PiANCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KKipTdGVwIDQ6IENhbGN1bGF0ZSBhbmQgQ29tcGFyZSBNZWFuIEV4dHJhIFRpbWUqKg0KDQpUbyBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgYW1vdW50IG9mIGV4dHJhIHRpbWUgcmVwb3J0ZWQgYnkgdXJiYW4gYW5kIHJ1cmFsIHJlc3BvbmRlbnRzLCB3ZSB1c2UgdGhlIGBzdW1tYXJpc2UoKWAgZnVuY3Rpb246DQoNCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgcmVzdWx0cyA9ICdoaWRlJ30NCm1lYW5fdXJiYW4gPC0gdXJiYW5fc2VsZWN0ZWQgfD4gDQogIHN1bW1hcmlzZShhdmdfZXh0cmFfdGltZSA9IG1lYW4oZXh0cmFUaW1lLCBuYS5ybSA9IFRSVUUpKQ0KbWVhbl9ydXJhbCA8LSBydXJhbF9zZWxlY3RlZCB8PiANCiAgc3VtbWFyaXNlKGF2Z19leHRyYV90aW1lID0gbWVhbihleHRyYVRpbWUsIG5hLnJtID0gVFJVRSkpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpwcmludCgiTWVhbiBleHRyYSB0aW1lIGZvciBVcmJhbiByZXNwb25kZW50czoiKQ0KbWVhbl91cmJhbiB8PiBrYmwoKSB8PiBrYWJsZV9zdHlsaW5nKCkNCg0KcHJpbnQoIk1lYW4gZXh0cmEgdGltZSBmb3IgUnVyYWwgcmVzcG9uZGVudHM6IikNCm1lYW5fcnVyYWwgfD4ga2JsKCkgfD4ga2FibGVfc3R5bGluZygpDQpgYGANCg0KSXQgc2VlbXMgdGhhdCwgb24gYXZlcmFnZSwgcmVzcG9uZGVudHMgcmVzaWRpbmcgaW4gcnVyYWwgYXJlYXMgaGF2ZSBtb3JlIGBleHRyYVRpbWVgLiBIaWdoZXIgdmFsdWVzIGluIGBleHRyYVRpbWVgIGluZGljYXRlIGdyZWF0ZXIgYXZhaWxhYmlsaXR5IG9mIGZyZWUgdGltZS4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgVGFrZWF3YXkNCg0KSW4gdGhpcyBzZXNzaW9uLCB3ZSBsZWFybmVkIGhvdyB0byBmaWx0ZXIgcm93cyBhbmQgc2VsZWN0IHNwZWNpZmljIGNvbHVtbnMgdG8gY3JlYXRlIGEgc21hbGxlciwgY2xlYW5lciBkYXRhc2V0IHRoYXQgaGVscHMgdXMgYW5zd2VyIG91ciBndWlkaW5nIHF1ZXN0aW9uIG1vcmUgZWZmaWNpZW50bHkuIFVzaW5nIGZ1bmN0aW9ucyBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UgbGlrZSBgZmlsdGVyKClgLCBgc2VsZWN0KClgLCBhbmQgYG11dGF0ZSgpYCwgd2UgcHJhY3RpY2VkIGhvdyB0bzoNCg0KLSAgIEZpbHRlciByb3dzIGJhc2VkIG9uIGNvbmRpdGlvbnMgdXNpbmcgY29tcGFyaXNvbiBhbmQgbG9naWNhbCBvcGVyYXRvcnNcDQotICAgU2VsZWN0IG9ubHkgdGhlIHZhcmlhYmxlcyB3ZSBjYXJlIGFib3V0IHVzaW5nIGBzZWxlY3QoKWAgYW5kIGhlbHBlciBmdW5jdGlvbnMgbGlrZSBgc3RhcnRzX3dpdGgoKWBcDQotICAgVXNlIGBzdW1tYXJpc2UoKWAgYW5kIGBhY3Jvc3MoKWAgdG8gY2FsY3VsYXRlIG1lYW4gdmFsdWVzIGZvciBlbnRpcmUgZ3JvdXBzXA0KLSAgIFJlY29kZSB2YXJpYWJsZXMgbGlrZSBgcG9wQ2VudGVyYCBhbmQgY3JlYXRlIG5ldyBmbGFncyBsaWtlIGBpc0ZlZWxSdXNoZWRgIHdpdGggYG11dGF0ZSgpYA0KDQpUaGVzZSB0ZWNobmlxdWVzIGhlbHAgdXMgem9vbSBpbiBvbiB0aGUgcGFydHMgb2YgdGhlIGRhdGEgdGhhdCBtYXR0ZXIgbW9zdCBhbmQgcHJlcGFyZSBmb3IgZGVlcGVyIGV4cGxvcmF0aW9uIGluIHRoZSBuZXh0IHNlc3Npb25zLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg==