Discrete-time survival analysis using logit models are different from
the Cox proportional hazards models in the last lecture:
1. How Time is Treated:
- Discrete-Time Logit Models are specifically
designed for situations where time is measured in discrete intervals
(e.g., years, months, grades in school). The event (e.g., promotion,
dropout) is considered to occur within one of these distinct time
periods. The data is structured in “person-period” format, where each
individual contributes multiple rows, one for each time period they are
at risk. The outcome variable is binary, indicating whether the event
occurred in that specific interval (1) or not (0), given they survived
until the beginning of the interval.
- Cox Proportional Hazards (Cox PH) Models are
designed for continuous time. They analyze the hazard rate, which is the
instantaneous risk of the event occurring at a specific point in time,
given that the individual has survived up to that time.3 While Cox
models can handle tied event times (multiple events occurring at the
same recorded time), they fundamentally assume an underlying continuous
time process.
2. The Outcome Variable and Modeling Approach:
- Discrete-Time Logit Models model the probability of
the event occurring within a specific discrete time interval,
conditional on survival up to the start of that interval. This
probability is typically modeled using a logistic regression framework,
hence the “logit” in the name. The log-odds of the event occurring in
each interval are predicted by the covariates.
- Cox PH Models directly models the hazard rate. It
estimates the hazard ratio, which is the ratio of the hazard rates
between two groups or for a one-unit change in a covariate. The model is
semi-parametric because it estimates the effect of covariates on the
hazard without making assumptions about the shape of the baseline hazard
function (the hazard when all covariates are zero).
3. The Baseline Hazard:
- In Discrete-Time Logit Models, the baseline hazard
(the probability of the event in each time interval when all covariates
are zero) is often treated as time-dependent. In the example code below,
this is implicitly handled by including
time
and
I(time^2)
as predictors. More explicitly, one could include
dummy variables for each time period to allow the baseline hazard to
vary freely across time.
- The Cox PH models has a non-parametric baseline
hazard that is not explicitly estimated. The focus is on how the
covariates proportionally scale this baseline hazard. The “proportional
hazards” assumption implies that the ratio of the hazard rates for any
two individuals remains constant over time.
4. The Proportional Hazards Assumption:
- While the term “discrete-time proportional hazards
model” exists (often using a complementary log-log link), the
logit model itself does not inherently assume proportional hazards. The
effects of covariates can vary across different time intervals,
especially when time-by-covariate interactions are included in the
model.
- A key assumption of the Cox model is that the
hazards are proportional over time. This means that the effect of a
covariate on the hazard rate is constant across all time points.
Violations of this assumption can lead to biased results. Statistical
tests and graphical methods are used to assess this assumption.
5. Handling of Ties:
- Discrete-Time Logit Models: Tied events (multiple
events occurring at the same recorded time) are naturally handled within
the discrete time intervals. If multiple promotions happen in the same
year, they all contribute to the event count for that year.
- Cox PH Models: Tied event times require specific
methods to handle them in the partial likelihood estimation (e.g.,
Breslow, Efron methods).
6. Data Structure:
- Discrete-Time Logit Models requires reshaping the
data into a long (person-period) format.
- Cox PH Models__ typically use a data structure where each row
represents an individual with their survival time and event status.
Time |
Discrete intervals |
Continuous |
Outcome |
Probability of event in an interval |
Hazard rate |
Modeling Approach |
Logistic Regression |
Semi-parametric hazard regression |
Baseline Hazard |
Can be time-dependent |
Non-parametric, not directly estimated |
Proportional Hazards |
Not inherently assumed |
Key assumption |
Handling Ties |
Naturally handled within intervals |
Requires specific tie-handling methods |
Data Structure |
Long (person-period) |
Typically one row per subject |
When to Choose Which:
- Choose Discrete-Time Logit Models when:
- Time is naturally measured in discrete intervals.
- The proportional hazards assumption of the Cox model is
violated.
- You want to directly model the probability of the event in each time
interval.
- You want more flexibility in how the effects of covariates change
over time.
- Choose Cox Proportional Hazards Models when:
- Time is continuous or can be reasonably approximated as
continuous.
- The proportional hazards assumption holds.
- You are primarily interested in estimating hazard ratios.
- You have a large dataset with many unique event times.
Load Libraries
library(tidyverse)
: This line loads the tidyverse package. Think
of this as bringing in your essential toolkit for data manipulation and
visualization in R. It includes powerful functions like
mutate
, pivot_longer
, left_join
,
and filter
, which we’ll see in action.
- library(haven): The haven
package is specifically designed to read data files from various
statistical software packages, including SAS, SPSS, and Stata. In this
case, we’ll use it to read a SAS dataset.
library(tidyverse)
library(haven)
Read in the data
The read_sas()
function from the haven package reads the
SAS dataset located at the provided URL and stores it in a data frame
called rank. This data contains information about associate professors,
their characteristics, and their career progression.
rank <- read_sas("https://statisticalhorizons.com/wp-content/uploads/2022/01/rank.sas7bdat")
#assign id numbers to the 301 case:
rank <- rank %>% mutate(id =1:301)
Here, we’re using the pipe operator %>%
from
dplyr
(part of tidyverse
). It allows us to
chain operations together in a readable way.
- mutate()
is a function that adds new variables or
modifies existing ones in a data frame.
- id = 1:301
creates a new variable named id and assigns a
unique integer from 1 to 301 to each row. This is important for tracking
individual cases, especially when we reshape the data later. We’re
essentially giving each person in the dataset a unique identifier.
Reshape the data from wide to long for articles
Why reshape to long format? In discrete-time survival analysis, we
are interested in the probability of an event (promotion in this case)
occurring at each discrete time point. The long format allows us to have
each person-year as a separate row, which is the structure needed for
fitting our logit model.
rankyrs <- rank %>%
pivot_longer(
cols = starts_with("art"),
names_to = "atime",
values_to = "art"
) %>%
mutate(time = as.numeric(str_remove(atime, "art")))
#reshape the data from wide to long for citations:
rankyrs2 <- rank %>%
pivot_longer(
cols = starts_with("cit"),
names_to = "ctime",
values_to = "cit"
) %>%
mutate(time = as.numeric(str_remove(ctime, "cit")))
pivot_longer()
is a powerful function for reshaping
data from a “wide” format (where each time point has its own column) to
a “long” format (where each row represents a specific time point for an
individual). This is crucial for discrete-time survival analysis.
cols = starts_with("art")
specifies that we want to
gather the columns whose names start with “art”. These represent the
number of articles published at different time points (e.g., art1, art2,
art3…).
names_to = "atime"
creates a new column named “atime”
that will contain the original column names (“art1”, “art2”,
etc.).
values_to = "art"
creates a new column named “art” that
will contain the values from the original “art” columns (the actual
number of articles).
The same logic applies to the block for citations
(starts_with("cit")
), creating ctime
and
cit
columns.
mutate(time = as.numeric(str_remove(atime, "art"))
):
After pivoting, the atime
column (e.g., “art1”, “art2”) is
still in character format. We want to extract the time information (the
number).
str_remove(atime, "art")
from the stringr
package (part of tidyverse
) removes the “art” prefix from
the atime
column, leaving just the number.
as.numeric()
converts this remaining string into a
numeric value, which we store in a new column called time
.
We do the same for the citations.
Merge articles and citations
rankyrs <- rankyrs %>%
left_join(rankyrs2 %>% select(id, time, cit), by = c("id", "time"))
left_join():
This function combines two data frames
based on common columns. We are merging rankyrs
(which now
has the article information in long format) with a subset of
rankyrs2
(which has the citation information in long
format).
rankyrs2 %>% select(id, time, cit)
: We first select
only the id
, time
, and cit
columns from rankyrs2
since these are the variables we want
to join.
by = c("id", "time")
: This specifies that the join
should be based on matching values in both the id
and
time
columns. This ensures that the number of articles and
citations for the same individual at the same time point are combined
into a single row in rankyrs
.
Sort by id and time
rankyrs<-rankyrs %>%
arrange(id, time)
arrange(id, time)
sorts the rankyrs
data
frame first by the id
column (so all observations for the
same individual are together) and then by the time
column
(so the observations for each individual are in chronological order).
This is helpful for understanding the sequence of events.
Recode event variable to be 0 before event occurrence
In discrete-time survival analysis, we model the probability of the
event occurring in a specific time interval, given that it has not
occurred before. Our promo variable captures this: for each person-year,
it’s 1 if the promotion happened at that exact time, and 0
otherwise.
ranksub <- ranksub %>%
mutate(promo = if_else(time == dur, event, 0))
mutate(promo = if_else(time == dur, event, 0))
creates
a new binary outcome variable called promo
.
if_else(condition, value_if_true, value_if_false)
is a
conditional function.
time == dur
is the condition checked to determine
whether the current time point (time
) is equal to the time
of the event (dur
).
event
: If the condition is true (i.e., we are at the
time of promotion), the value of promo
is set to the value
of the event
variable (which is 1 for promotion and 0
otherwise).
- If the condition is false (i.e., we are at a time point before the
promotion), the value of promo is set to
0
, indicating that
the event did not occur at that specific person-year.
Change the missings in jobtime to 11
The choice of 11 might be based on the context of the data (e.g., the
maximum observed job time plus one.
ranksub <- ranksub %>%
mutate(jobtime = if_else(is.na(jobtime), 11, jobtime))
mutate(jobtime = if_else(is.na(jobtime), 11, jobtime))
:
is.na(jobtime)
checks for missing values (represented
as NA
) in the jobtime
column.
- If a value is missing (TRUE), it’s replaced with 11.
- If a value is not missing (FALSE), it remains its original
value.
Define jobpres, a time-varying coviariate
This demonstrates how to handle time-varying covariates in
discrete-time survival analysis. The value of jobpres
can
change for an individual as time progresses, and our data structure in
long format allows us to capture these changes.
ranksub <- ranksub %>%
mutate(jobpres = if_else(time<jobtime,prest1, prest2))
mutate(jobpres = if_else(time < jobtime, prest1, prest2))
creates a new variable jobpres
.
time < jobtime
is the condition checked where the
current time point (time) is less than the jobtime.
- If the condition is true (i.e., before
jobtime
), the
value of jobpres
is taken from the prest1
variable (likely prestige at an earlier job).
- If the condition is false (i.e., at or after
jobtime
),
the value of jobpres
is taken from the prest2
variable (likely prestige at a later or current job).
Fit discrete time logit
This glm()
call fits a discrete-time logit model where
the outcome is the log-odds of promotion at a specific time point. The
coefficients from this model will tell us how each predictor variable is
associated with this log-odds, holding other variables constant. We will
need to exponentiate these coefficients to interpret them as odds
ratios.
myfit<-glm(promo~undgrad + phdmed + phdprest + art + cit + jobpres + time + I(time^2), family=binomial,data=ranksub)
library(gtsummary)
# Create a summary table of the model results
model_table <- tbl_regression(
myfit,
exponentiate = TRUE, # Display odds ratios instead of log-odds
pvalue_fun = ~style_pvalue(.x, digits = 3), # Format p-values
label = list(
"undgrad" ~ "Undergraduate Institution Prestige",
"phdmed" ~ "Medical PhD",
"phdprest" ~ "PhD Program Prestige",
"art" ~ "Number of Articles",
"cit" ~ "Number of Citations",
"jobpres" ~ "Job Prestige",
"time" ~ "Time (Years)",
"I(time^2)" ~ "Time Squared" # Use the exact string "I(time^2)"
) # Custom labels for variables
)
# Print the table to the console
model_table
Characteristic |
OR |
95% CI |
p-value |
Undergraduate Institution Prestige |
1.21 |
1.07, 1.38 |
0.002 |
Medical PhD |
0.79 |
0.56, 1.11 |
0.170 |
PhD Program Prestige |
1.03 |
0.86, 1.23 |
0.772 |
Number of Articles |
1.08 |
1.04, 1.12 |
<0.001 |
Number of Citations |
1.00 |
1.00, 1.00 |
0.924 |
Job Prestige |
0.78 |
0.62, 0.97 |
0.026 |
Time (Years) |
8.02 |
5.18, 13.0 |
<0.001 |
Time Squared |
0.85 |
0.82, 0.89 |
<0.001 |
Abbreviations: CI = Confidence Interval, OR = Odds Ratio |
# You can further customize the table, for example, adding a title and caption
model_table <- model_table %>%
as_gt() %>% # Convert to a gt table for more advanced formatting
gt::tab_header(
title = "Discrete-Time Logit Model Results",
subtitle = "Predicting Probability of Promotion"
) %>%
gt::tab_caption("Odds ratios and p-values are reported.")
# Print the customized table
model_table
LS0tDQp0aXRsZTogIkRpc2NyZXRlIFRpbWUgTG9naXQgTW9kZWxzIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KRGlzY3JldGUtdGltZSBzdXJ2aXZhbCBhbmFseXNpcyB1c2luZyBsb2dpdCBtb2RlbHMgYXJlIGRpZmZlcmVudCBmcm9tIHRoZSBDb3ggcHJvcG9ydGlvbmFsIGhhemFyZHMgbW9kZWxzIGluIHRoZSBsYXN0IGxlY3R1cmU6DQoNCiMjIyAxLiBIb3cgVGltZSBpcyBUcmVhdGVkOg0KIC0gX19EaXNjcmV0ZS1UaW1lIExvZ2l0IE1vZGVsc19fIGFyZSBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIHNpdHVhdGlvbnMgd2hlcmUgdGltZSBpcyBtZWFzdXJlZCBpbiBkaXNjcmV0ZSBpbnRlcnZhbHMgKGUuZy4sIHllYXJzLCBtb250aHMsIGdyYWRlcyBpbiBzY2hvb2wpLiBUaGUgZXZlbnQgKGUuZy4sIHByb21vdGlvbiwgZHJvcG91dCkgaXMgY29uc2lkZXJlZCB0byBvY2N1ciB3aXRoaW4gb25lIG9mIHRoZXNlIGRpc3RpbmN0IHRpbWUgcGVyaW9kcy4gVGhlIGRhdGEgaXMgc3RydWN0dXJlZCBpbiAicGVyc29uLXBlcmlvZCIgZm9ybWF0LCB3aGVyZSBlYWNoIGluZGl2aWR1YWwgY29udHJpYnV0ZXMgbXVsdGlwbGUgcm93cywgb25lIGZvciBlYWNoIHRpbWUgcGVyaW9kIHRoZXkgYXJlIGF0IHJpc2suIFRoZSBvdXRjb21lIHZhcmlhYmxlIGlzIGJpbmFyeSwgaW5kaWNhdGluZyB3aGV0aGVyIHRoZSBldmVudCBvY2N1cnJlZCBpbiB0aGF0IHNwZWNpZmljIGludGVydmFsICgxKSBvciBub3QgKDApLCBnaXZlbiB0aGV5IHN1cnZpdmVkIHVudGlsIHRoZSBiZWdpbm5pbmcgb2YgdGhlIGludGVydmFsLiAgDQogLSBfX0NveCBQcm9wb3J0aW9uYWwgSGF6YXJkcyAoQ294IFBIKSBNb2RlbHNfXyBhcmUgZGVzaWduZWQgZm9yIGNvbnRpbnVvdXMgdGltZS4gVGhleSBhbmFseXplIHRoZSBoYXphcmQgcmF0ZSwgd2hpY2ggaXMgdGhlIGluc3RhbnRhbmVvdXMgcmlzayBvZiB0aGUgZXZlbnQgb2NjdXJyaW5nIGF0IGEgc3BlY2lmaWMgcG9pbnQgaW4gdGltZSwgZ2l2ZW4gdGhhdCB0aGUgaW5kaXZpZHVhbCBoYXMgc3Vydml2ZWQgdXAgdG8gdGhhdCB0aW1lLjMgV2hpbGUgQ294IG1vZGVscyBjYW4gaGFuZGxlIHRpZWQgZXZlbnQgdGltZXMgKG11bHRpcGxlIGV2ZW50cyBvY2N1cnJpbmcgYXQgdGhlIHNhbWUgcmVjb3JkZWQgdGltZSksIHRoZXkgZnVuZGFtZW50YWxseSBhc3N1bWUgYW4gdW5kZXJseWluZyBjb250aW51b3VzIHRpbWUgcHJvY2Vzcy4NCiANCiMjIyAyLiBUaGUgT3V0Y29tZSBWYXJpYWJsZSBhbmQgTW9kZWxpbmcgQXBwcm9hY2g6DQogLSBfX0Rpc2NyZXRlLVRpbWUgTG9naXQgTW9kZWxzX18gbW9kZWwgdGhlIHByb2JhYmlsaXR5IG9mIHRoZSBldmVudCBvY2N1cnJpbmcgd2l0aGluIGEgc3BlY2lmaWMgZGlzY3JldGUgdGltZSBpbnRlcnZhbCwgY29uZGl0aW9uYWwgb24gc3Vydml2YWwgdXAgdG8gdGhlIHN0YXJ0IG9mIHRoYXQgaW50ZXJ2YWwuIFRoaXMgcHJvYmFiaWxpdHkgaXMgdHlwaWNhbGx5IG1vZGVsZWQgdXNpbmcgYSBsb2dpc3RpYyByZWdyZXNzaW9uIGZyYW1ld29yaywgaGVuY2UgdGhlICJsb2dpdCIgaW4gdGhlIG5hbWUuIFRoZSBsb2ctb2RkcyBvZiB0aGUgZXZlbnQgb2NjdXJyaW5nIGluIGVhY2ggaW50ZXJ2YWwgYXJlIHByZWRpY3RlZCBieSB0aGUgY292YXJpYXRlcy4gIA0KIC0gX19Db3ggUEggTW9kZWxzX18gZGlyZWN0bHkgbW9kZWxzIHRoZSBoYXphcmQgcmF0ZS4gSXQgZXN0aW1hdGVzIHRoZSBoYXphcmQgcmF0aW8sIHdoaWNoIGlzIHRoZSByYXRpbyBvZiB0aGUgaGF6YXJkIHJhdGVzIGJldHdlZW4gdHdvIGdyb3VwcyBvciBmb3IgYSBvbmUtdW5pdCBjaGFuZ2UgaW4gYSBjb3ZhcmlhdGUuIFRoZSBtb2RlbCBpcyBzZW1pLXBhcmFtZXRyaWMgYmVjYXVzZSBpdCBlc3RpbWF0ZXMgdGhlIGVmZmVjdCBvZiBjb3ZhcmlhdGVzIG9uIHRoZSBoYXphcmQgd2l0aG91dCBtYWtpbmcgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIHNoYXBlIG9mIHRoZSBiYXNlbGluZSBoYXphcmQgZnVuY3Rpb24gKHRoZSBoYXphcmQgd2hlbiBhbGwgY292YXJpYXRlcyBhcmUgemVybykuICANCiANCiMjIyAzLiBUaGUgQmFzZWxpbmUgSGF6YXJkOg0KIC0gSW4gX19EaXNjcmV0ZS1UaW1lIExvZ2l0IE1vZGVsc19fLCB0aGUgYmFzZWxpbmUgaGF6YXJkICh0aGUgcHJvYmFiaWxpdHkgb2YgdGhlIGV2ZW50IGluIGVhY2ggdGltZSBpbnRlcnZhbCB3aGVuIGFsbCBjb3ZhcmlhdGVzIGFyZSB6ZXJvKSBpcyBvZnRlbiB0cmVhdGVkIGFzIHRpbWUtZGVwZW5kZW50LiBJbiB0aGUgZXhhbXBsZSBjb2RlIGJlbG93LCB0aGlzIGlzIGltcGxpY2l0bHkgaGFuZGxlZCBieSBpbmNsdWRpbmcgYHRpbWVgIGFuZCBgSSh0aW1lXjIpYCBhcyBwcmVkaWN0b3JzLiBNb3JlIGV4cGxpY2l0bHksIG9uZSBjb3VsZCBpbmNsdWRlIGR1bW15IHZhcmlhYmxlcyBmb3IgZWFjaCB0aW1lIHBlcmlvZCB0byBhbGxvdyB0aGUgYmFzZWxpbmUgaGF6YXJkIHRvIHZhcnkgZnJlZWx5IGFjcm9zcyB0aW1lLiAgDQogLSBUaGUgX19Db3ggUEggbW9kZWxzX18gIGhhcyBhIG5vbi1wYXJhbWV0cmljIGJhc2VsaW5lIGhhemFyZCB0aGF0IGlzIG5vdCBleHBsaWNpdGx5IGVzdGltYXRlZC4gVGhlIGZvY3VzIGlzIG9uIGhvdyB0aGUgY292YXJpYXRlcyBwcm9wb3J0aW9uYWxseSBzY2FsZSB0aGlzIGJhc2VsaW5lIGhhemFyZC4gVGhlICJwcm9wb3J0aW9uYWwgaGF6YXJkcyIgYXNzdW1wdGlvbiBpbXBsaWVzIHRoYXQgdGhlIHJhdGlvIG9mIHRoZSBoYXphcmQgcmF0ZXMgZm9yIGFueSB0d28gaW5kaXZpZHVhbHMgcmVtYWlucyBjb25zdGFudCBvdmVyIHRpbWUuICANCiANCiMjIyA0LiBUaGUgUHJvcG9ydGlvbmFsIEhhemFyZHMgQXNzdW1wdGlvbjoNCiAtIFdoaWxlIHRoZSB0ZXJtIF9fImRpc2NyZXRlLXRpbWUgcHJvcG9ydGlvbmFsIGhhemFyZHMgbW9kZWwiX18gZXhpc3RzIChvZnRlbiB1c2luZyBhIGNvbXBsZW1lbnRhcnkgbG9nLWxvZyBsaW5rKSwgdGhlIGxvZ2l0IG1vZGVsIGl0c2VsZiBkb2VzIG5vdCBpbmhlcmVudGx5IGFzc3VtZSBwcm9wb3J0aW9uYWwgaGF6YXJkcy4gVGhlIGVmZmVjdHMgb2YgY292YXJpYXRlcyBjYW4gdmFyeSBhY3Jvc3MgZGlmZmVyZW50IHRpbWUgaW50ZXJ2YWxzLCBlc3BlY2lhbGx5IHdoZW4gdGltZS1ieS1jb3ZhcmlhdGUgaW50ZXJhY3Rpb25zIGFyZSBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuICAgIA0KIC0gQSBrZXkgYXNzdW1wdGlvbiBvZiBfX3RoZSBDb3ggbW9kZWxfXyBpcyB0aGF0IHRoZSBoYXphcmRzIGFyZSBwcm9wb3J0aW9uYWwgb3ZlciB0aW1lLiBUaGlzIG1lYW5zIHRoYXQgdGhlIGVmZmVjdCBvZiBhIGNvdmFyaWF0ZSBvbiB0aGUgaGF6YXJkIHJhdGUgaXMgY29uc3RhbnQgYWNyb3NzIGFsbCB0aW1lIHBvaW50cy4gVmlvbGF0aW9ucyBvZiB0aGlzIGFzc3VtcHRpb24gY2FuIGxlYWQgdG8gYmlhc2VkIHJlc3VsdHMuIFN0YXRpc3RpY2FsIHRlc3RzIGFuZCBncmFwaGljYWwgbWV0aG9kcyBhcmUgdXNlZCB0byBhc3Nlc3MgdGhpcyBhc3N1bXB0aW9uLiAgDQogDQojIyMgNS4gSGFuZGxpbmcgb2YgVGllczogIA0KIC0gX19EaXNjcmV0ZS1UaW1lIExvZ2l0IE1vZGVsczpfXyBUaWVkIGV2ZW50cyAobXVsdGlwbGUgZXZlbnRzIG9jY3VycmluZyBhdCB0aGUgc2FtZSByZWNvcmRlZCB0aW1lKSBhcmUgbmF0dXJhbGx5IGhhbmRsZWQgd2l0aGluIHRoZSBkaXNjcmV0ZSB0aW1lIGludGVydmFscy4gSWYgbXVsdGlwbGUgcHJvbW90aW9ucyBoYXBwZW4gaW4gdGhlIHNhbWUgeWVhciwgdGhleSBhbGwgY29udHJpYnV0ZSB0byB0aGUgZXZlbnQgY291bnQgZm9yIHRoYXQgeWVhci4gIA0KIC0gX19Db3ggUEggTW9kZWxzOl9fIFRpZWQgZXZlbnQgdGltZXMgcmVxdWlyZSBzcGVjaWZpYyBtZXRob2RzIHRvIGhhbmRsZSB0aGVtIGluIHRoZSBwYXJ0aWFsIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiAoZS5nLiwgQnJlc2xvdywgRWZyb24gbWV0aG9kcykuDQoNCiMjIyA2LiBEYXRhIFN0cnVjdHVyZToNCiAtIF9fRGlzY3JldGUtVGltZSBMb2dpdCBNb2RlbHNfXyByZXF1aXJlcyByZXNoYXBpbmcgdGhlIGRhdGEgaW50byBhIGxvbmcgKHBlcnNvbi1wZXJpb2QpIGZvcm1hdC4gIA0KIC0gQ294IFBIIE1vZGVsc19fIHR5cGljYWxseSB1c2UgYSBkYXRhIHN0cnVjdHVyZSB3aGVyZSBlYWNoIHJvdyByZXByZXNlbnRzIGFuIGluZGl2aWR1YWwgd2l0aCB0aGVpciBzdXJ2aXZhbCB0aW1lIGFuZCBldmVudCBzdGF0dXMuICANCiANCg0KfCBGZWF0dXJlICAgICAgICAgICAgICB8IERpc2NyZXRlLVRpbWUgTG9naXQgTW9kZWxzICAgICAgICAgIHwgQ294IFByb3BvcnRpb25hbCBIYXphcmRzIE1vZGVscyAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBUaW1lICAgICAgICAgICAgICAgICB8IERpc2NyZXRlIGludGVydmFscyAgICAgICAgICAgICAgICAgIHwgQ29udGludW91cyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBPdXRjb21lICAgICAgICAgICAgICB8IFByb2JhYmlsaXR5IG9mIGV2ZW50IGluIGFuIGludGVydmFsIHwgSGF6YXJkIHJhdGUgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBNb2RlbGluZyBBcHByb2FjaCAgICB8IExvZ2lzdGljIFJlZ3Jlc3Npb24gICAgICAgICAgICAgICAgIHwgU2VtaS1wYXJhbWV0cmljIGhhemFyZCByZWdyZXNzaW9uICAgICAgfA0KfCBCYXNlbGluZSBIYXphcmQgICAgICB8IENhbiBiZSB0aW1lLWRlcGVuZGVudCAgICAgICAgICAgICAgIHwgTm9uLXBhcmFtZXRyaWMsIG5vdCBkaXJlY3RseSBlc3RpbWF0ZWQgfA0KfCBQcm9wb3J0aW9uYWwgSGF6YXJkcyB8IE5vdCBpbmhlcmVudGx5IGFzc3VtZWQgICAgICAgICAgICAgIHwgS2V5IGFzc3VtcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBIYW5kbGluZyBUaWVzICAgICAgICB8IE5hdHVyYWxseSBoYW5kbGVkIHdpdGhpbiBpbnRlcnZhbHMgIHwgUmVxdWlyZXMgc3BlY2lmaWMgdGllLWhhbmRsaW5nIG1ldGhvZHMgfA0KfCBEYXRhIFN0cnVjdHVyZSAgICAgICB8IExvbmcgKHBlcnNvbi1wZXJpb2QpICAgICAgICAgICAgICAgIHwgVHlwaWNhbGx5IG9uZSByb3cgcGVyIHN1YmplY3QgICAgICAgICAgfCAgDQoNCiMjIFdoZW4gdG8gQ2hvb3NlIFdoaWNoOg0KIC0gQ2hvb3NlIERpc2NyZXRlLVRpbWUgTG9naXQgTW9kZWxzIHdoZW46ICANCiAgIC0gVGltZSBpcyBuYXR1cmFsbHkgbWVhc3VyZWQgaW4gZGlzY3JldGUgaW50ZXJ2YWxzLg0KICAgLSBUaGUgcHJvcG9ydGlvbmFsIGhhemFyZHMgYXNzdW1wdGlvbiBvZiB0aGUgQ294IG1vZGVsIGlzIHZpb2xhdGVkLiAgDQogICAtIFlvdSB3YW50IHRvIGRpcmVjdGx5IG1vZGVsIHRoZSBwcm9iYWJpbGl0eSBvZiB0aGUgZXZlbnQgaW4gZWFjaCB0aW1lIGludGVydmFsLiAgDQogICAtIFlvdSB3YW50IG1vcmUgZmxleGliaWxpdHkgaW4gaG93IHRoZSBlZmZlY3RzIG9mIGNvdmFyaWF0ZXMgY2hhbmdlIG92ZXIgdGltZS4gIA0KIC0gQ2hvb3NlIENveCBQcm9wb3J0aW9uYWwgSGF6YXJkcyBNb2RlbHMgd2hlbjogIA0KICAgLSBUaW1lIGlzIGNvbnRpbnVvdXMgb3IgY2FuIGJlIHJlYXNvbmFibHkgYXBwcm94aW1hdGVkIGFzIGNvbnRpbnVvdXMuICANCiAgIC0gVGhlIHByb3BvcnRpb25hbCBoYXphcmRzIGFzc3VtcHRpb24gaG9sZHMuICANCiAgIC0gWW91IGFyZSBwcmltYXJpbHkgaW50ZXJlc3RlZCBpbiBlc3RpbWF0aW5nIGhhemFyZCByYXRpb3MuICANCiAgIC0gWW91IGhhdmUgYSBsYXJnZSBkYXRhc2V0IHdpdGggbWFueSB1bmlxdWUgZXZlbnQgdGltZXMuICANCg0KIyMgTG9hZCBMaWJyYXJpZXMNCiAtIGBsaWJyYXJ5KHRpZHl2ZXJzZSlgOiBUaGlzIGxpbmUgbG9hZHMgdGhlIFt0aWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvcGFja2FnZXMvKSBwYWNrYWdlLiBUaGluayBvZiB0aGlzIGFzIGJyaW5naW5nIGluIHlvdXIgZXNzZW50aWFsIHRvb2xraXQgZm9yIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCB2aXN1YWxpemF0aW9uIGluIFIuIEl0IGluY2x1ZGVzIHBvd2VyZnVsIGZ1bmN0aW9ucyBsaWtlIGBtdXRhdGVgLCBgcGl2b3RfbG9uZ2VyYCwgYGxlZnRfam9pbmAsIGFuZCBgZmlsdGVyYCwgd2hpY2ggd2UnbGwgc2VlIGluIGFjdGlvbi4gICAgDQogLSBsaWJyYXJ5KGhhdmVuKTogVGhlIFtoYXZlbl0oaHR0cHM6Ly9oYXZlbi50aWR5dmVyc2Uub3JnLykgcGFja2FnZSBpcyBzcGVjaWZpY2FsbHkgZGVzaWduZWQgdG8gcmVhZCBkYXRhIGZpbGVzIGZyb20gdmFyaW91cyBzdGF0aXN0aWNhbCBzb2Z0d2FyZSBwYWNrYWdlcywgaW5jbHVkaW5nIFNBUywgU1BTUywgYW5kIFN0YXRhLiBJbiB0aGlzIGNhc2UsIHdlJ2xsIHVzZSBpdCB0byByZWFkIGEgU0FTIGRhdGFzZXQuICANCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGhhdmVuKQ0KYGBgDQoNCiMjIFJlYWQgaW4gdGhlIGRhdGENClRoZSBgcmVhZF9zYXMoKWAgZnVuY3Rpb24gZnJvbSB0aGUgaGF2ZW4gcGFja2FnZSByZWFkcyB0aGUgU0FTIGRhdGFzZXQgbG9jYXRlZCBhdCB0aGUgcHJvdmlkZWQgVVJMIGFuZCBzdG9yZXMgaXQgaW4gYSBkYXRhIGZyYW1lIGNhbGxlZCByYW5rLiBUaGlzIGRhdGEgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgYXNzb2NpYXRlIHByb2Zlc3NvcnMsIHRoZWlyIGNoYXJhY3RlcmlzdGljcywgYW5kIHRoZWlyIGNhcmVlciBwcm9ncmVzc2lvbi4NCg0KYGBge3J9DQpyYW5rIDwtIHJlYWRfc2FzKCJodHRwczovL3N0YXRpc3RpY2FsaG9yaXpvbnMuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzAxL3Jhbmsuc2FzN2JkYXQiKQ0KDQojYXNzaWduIGlkIG51bWJlcnMgdG8gdGhlIDMwMSBjYXNlOg0KcmFuayA8LSByYW5rICU+JSBtdXRhdGUoaWQgPTE6MzAxKQ0KYGBgDQpIZXJlLCB3ZSdyZSB1c2luZyB0aGUgcGlwZSBvcGVyYXRvciBgJT4lYCBmcm9tIGBkcGx5cmAgKHBhcnQgb2YgYHRpZHl2ZXJzZWApLiBJdCBhbGxvd3MgdXMgdG8gY2hhaW4gb3BlcmF0aW9ucyB0b2dldGhlciBpbiBhIHJlYWRhYmxlIHdheS4gIA0KIC0gYG11dGF0ZSgpYCBpcyBhIGZ1bmN0aW9uIHRoYXQgYWRkcyBuZXcgdmFyaWFibGVzIG9yIG1vZGlmaWVzIGV4aXN0aW5nIG9uZXMgaW4gYSBkYXRhIGZyYW1lLiAgDQogLSBgaWQgPSAxOjMwMWAgY3JlYXRlcyBhIG5ldyB2YXJpYWJsZSBuYW1lZCBpZCBhbmQgYXNzaWducyBhIHVuaXF1ZSBpbnRlZ2VyIGZyb20gMSB0byAzMDEgdG8gZWFjaCByb3cuIFRoaXMgaXMgaW1wb3J0YW50IGZvciB0cmFja2luZyBpbmRpdmlkdWFsIGNhc2VzLCBlc3BlY2lhbGx5IHdoZW4gd2UgcmVzaGFwZSB0aGUgZGF0YSBsYXRlci4gV2UncmUgZXNzZW50aWFsbHkgZ2l2aW5nIGVhY2ggcGVyc29uIGluIHRoZSBkYXRhc2V0IGEgdW5pcXVlIGlkZW50aWZpZXIuICANCg0KIyMgUmVzaGFwZSB0aGUgZGF0YSBmcm9tIHdpZGUgdG8gbG9uZyBmb3IgYXJ0aWNsZXMNCldoeSByZXNoYXBlIHRvIGxvbmcgZm9ybWF0PyBJbiBkaXNjcmV0ZS10aW1lIHN1cnZpdmFsIGFuYWx5c2lzLCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgcHJvYmFiaWxpdHkgb2YgYW4gZXZlbnQgKHByb21vdGlvbiBpbiB0aGlzIGNhc2UpIG9jY3VycmluZyBhdCBlYWNoIGRpc2NyZXRlIHRpbWUgcG9pbnQuIFRoZSBsb25nIGZvcm1hdCBhbGxvd3MgdXMgdG8gaGF2ZSBlYWNoIHBlcnNvbi15ZWFyIGFzIGEgc2VwYXJhdGUgcm93LCB3aGljaCBpcyB0aGUgc3RydWN0dXJlIG5lZWRlZCBmb3IgZml0dGluZyBvdXIgbG9naXQgbW9kZWwuICANCg0KYGBge3J9DQpyYW5reXJzIDwtIHJhbmsgJT4lDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gc3RhcnRzX3dpdGgoImFydCIpLA0KICAgIG5hbWVzX3RvICA9ICJhdGltZSIsDQogICAgdmFsdWVzX3RvID0gImFydCINCiAgKSAlPiUNCiAgbXV0YXRlKHRpbWUgPSBhcy5udW1lcmljKHN0cl9yZW1vdmUoYXRpbWUsICJhcnQiKSkpDQoNCiNyZXNoYXBlIHRoZSBkYXRhIGZyb20gd2lkZSB0byBsb25nIGZvciBjaXRhdGlvbnM6DQpyYW5reXJzMiA8LSByYW5rICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IHN0YXJ0c193aXRoKCJjaXQiKSwgDQogICAgbmFtZXNfdG8gPSAiY3RpbWUiLA0KICAgIHZhbHVlc190byA9ICJjaXQiDQogICkgJT4lDQogIG11dGF0ZSh0aW1lID0gYXMubnVtZXJpYyhzdHJfcmVtb3ZlKGN0aW1lLCAiY2l0IikpKQ0KYGBgDQoNCiAtIGBwaXZvdF9sb25nZXIoKWAgaXMgYSBwb3dlcmZ1bCBmdW5jdGlvbiBmb3IgcmVzaGFwaW5nIGRhdGEgZnJvbSBhICJ3aWRlIiBmb3JtYXQgKHdoZXJlIGVhY2ggdGltZSBwb2ludCBoYXMgaXRzIG93biBjb2x1bW4pIHRvIGEgImxvbmciIGZvcm1hdCAod2hlcmUgZWFjaCByb3cgcmVwcmVzZW50cyBhIHNwZWNpZmljIHRpbWUgcG9pbnQgZm9yIGFuIGluZGl2aWR1YWwpLiBUaGlzIGlzIGNydWNpYWwgZm9yIGRpc2NyZXRlLXRpbWUgc3Vydml2YWwgYW5hbHlzaXMuICANCiANCiAgIC0gYGNvbHMgPSBzdGFydHNfd2l0aCgiYXJ0IilgIHNwZWNpZmllcyB0aGF0IHdlIHdhbnQgdG8gZ2F0aGVyIHRoZSBjb2x1bW5zIHdob3NlIG5hbWVzIHN0YXJ0IHdpdGggImFydCIuIFRoZXNlIHJlcHJlc2VudCB0aGUgbnVtYmVyIG9mIGFydGljbGVzIHB1Ymxpc2hlZCBhdCBkaWZmZXJlbnQgdGltZSBwb2ludHMgKGUuZy4sIGFydDEsIGFydDIsIGFydDMuLi4pLiAgDQogICAtIGBuYW1lc190byA9ICJhdGltZSJgIGNyZWF0ZXMgYSBuZXcgY29sdW1uIG5hbWVkICJhdGltZSIgdGhhdCB3aWxsIGNvbnRhaW4gdGhlIG9yaWdpbmFsIGNvbHVtbiBuYW1lcyAoImFydDEiLCAiYXJ0MiIsIGV0Yy4pLiAgDQogICAtIGB2YWx1ZXNfdG8gPSAiYXJ0ImAgY3JlYXRlcyBhIG5ldyBjb2x1bW4gbmFtZWQgImFydCIgdGhhdCB3aWxsIGNvbnRhaW4gdGhlIHZhbHVlcyBmcm9tIHRoZSBvcmlnaW5hbCAiYXJ0IiBjb2x1bW5zICh0aGUgYWN0dWFsIG51bWJlciBvZiBhcnRpY2xlcykuICANCiAtIFRoZSBzYW1lIGxvZ2ljIGFwcGxpZXMgdG8gdGhlIGJsb2NrIGZvciBjaXRhdGlvbnMgKGBzdGFydHNfd2l0aCgiY2l0IilgKSwgY3JlYXRpbmcgYGN0aW1lYCBhbmQgYGNpdGAgY29sdW1ucy4gIA0KIC0gbXV0YXRlKGB0aW1lID0gYXMubnVtZXJpYyhzdHJfcmVtb3ZlKGF0aW1lLCAiYXJ0IikpYCk6IEFmdGVyIHBpdm90aW5nLCB0aGUgYGF0aW1lYCBjb2x1bW4gKGUuZy4sICJhcnQxIiwgImFydDIiKSBpcyBzdGlsbCBpbiBjaGFyYWN0ZXIgZm9ybWF0LiBXZSB3YW50IHRvIGV4dHJhY3QgdGhlIHRpbWUgaW5mb3JtYXRpb24gKHRoZSBudW1iZXIpLiAgDQogICAtIGBzdHJfcmVtb3ZlKGF0aW1lLCAiYXJ0IilgIGZyb20gdGhlIGBzdHJpbmdyYCBwYWNrYWdlIChwYXJ0IG9mIGB0aWR5dmVyc2VgKSByZW1vdmVzIHRoZSAiYXJ0IiBwcmVmaXggZnJvbSB0aGUgYGF0aW1lYCBjb2x1bW4sIGxlYXZpbmcganVzdCB0aGUgbnVtYmVyLiAgDQogICAtIGBhcy5udW1lcmljKClgIGNvbnZlcnRzIHRoaXMgcmVtYWluaW5nIHN0cmluZyBpbnRvIGEgbnVtZXJpYyB2YWx1ZSwgd2hpY2ggd2Ugc3RvcmUgaW4gYSBuZXcgY29sdW1uIGNhbGxlZCBgdGltZWAuIFdlIGRvIHRoZSBzYW1lIGZvciB0aGUgY2l0YXRpb25zLiAgDQoNCiMjIE1lcmdlIGFydGljbGVzIGFuZCBjaXRhdGlvbnMNCmBgYHtyfQ0KcmFua3lycyA8LSByYW5reXJzICU+JQ0KICBsZWZ0X2pvaW4ocmFua3lyczIgJT4lIHNlbGVjdChpZCwgdGltZSwgY2l0KSwgYnkgPSBjKCJpZCIsICJ0aW1lIikpDQpgYGANCg0KIC0gYGxlZnRfam9pbigpOmAgVGhpcyBmdW5jdGlvbiBjb21iaW5lcyB0d28gZGF0YSBmcmFtZXMgYmFzZWQgb24gY29tbW9uIGNvbHVtbnMuIFdlIGFyZSBtZXJnaW5nIGByYW5reXJzYCAod2hpY2ggbm93IGhhcyB0aGUgYXJ0aWNsZSBpbmZvcm1hdGlvbiBpbiBsb25nIGZvcm1hdCkgd2l0aCBhIHN1YnNldCBvZiBgcmFua3lyczJgICh3aGljaCBoYXMgdGhlIGNpdGF0aW9uIGluZm9ybWF0aW9uIGluIGxvbmcgZm9ybWF0KS4gIA0KICAgLSBgcmFua3lyczIgJT4lIHNlbGVjdChpZCwgdGltZSwgY2l0KWA6IFdlIGZpcnN0IHNlbGVjdCBvbmx5IHRoZSBgaWRgLCBgdGltZWAsIGFuZCBgY2l0YCBjb2x1bW5zIGZyb20gYHJhbmt5cnMyYCBzaW5jZSB0aGVzZSBhcmUgdGhlIHZhcmlhYmxlcyB3ZSB3YW50IHRvIGpvaW4uICANCiAgIC0gYGJ5ID0gYygiaWQiLCAidGltZSIpYDogVGhpcyBzcGVjaWZpZXMgdGhhdCB0aGUgam9pbiBzaG91bGQgYmUgYmFzZWQgb24gbWF0Y2hpbmcgdmFsdWVzIGluIGJvdGggdGhlIGBpZGAgYW5kIGB0aW1lYCBjb2x1bW5zLiBUaGlzIGVuc3VyZXMgdGhhdCB0aGUgbnVtYmVyIG9mIGFydGljbGVzIGFuZCBjaXRhdGlvbnMgZm9yIHRoZSBzYW1lIGluZGl2aWR1YWwgYXQgdGhlIHNhbWUgdGltZSBwb2ludCBhcmUgY29tYmluZWQgaW50byBhIHNpbmdsZSByb3cgaW4gYHJhbmt5cnNgLiAgDQoNCiMjIFNvcnQgYnkgaWQgYW5kIHRpbWUNCmBgYHtyfQ0KcmFua3lyczwtcmFua3lycyAlPiUNCiAgYXJyYW5nZShpZCwgdGltZSkNCmBgYA0KDQogLSBgYXJyYW5nZShpZCwgdGltZSlgIHNvcnRzIHRoZSBgcmFua3lyc2AgZGF0YSBmcmFtZSBmaXJzdCBieSB0aGUgYGlkYCBjb2x1bW4gKHNvIGFsbCBvYnNlcnZhdGlvbnMgZm9yIHRoZSBzYW1lIGluZGl2aWR1YWwgYXJlIHRvZ2V0aGVyKSBhbmQgdGhlbiBieSB0aGUgYHRpbWVgIGNvbHVtbiAoc28gdGhlIG9ic2VydmF0aW9ucyBmb3IgZWFjaCBpbmRpdmlkdWFsIGFyZSBpbiBjaHJvbm9sb2dpY2FsIG9yZGVyKS4gVGhpcyBpcyBoZWxwZnVsIGZvciB1bmRlcnN0YW5kaW5nIHRoZSBzZXF1ZW5jZSBvZiBldmVudHMuICANCg0KIyMgR2V0IHJpZCBvZiBwZXJzb24geWVhcnMgYWZ0ZXIgYSBwcm9tb3Rpb24gIA0KVGhpcyBmaWx0ZXJpbmcgY3JlYXRlcyB0aGUgInJpc2sgc2V0IiBhdCBlYWNoIHRpbWUgcG9pbnQuIEZvciBlYWNoIGluZGl2aWR1YWwgYXQgZWFjaCB0aW1lIHBvaW50IGJlZm9yZSBvciBhdCB0aGUgdGltZSBvZiB0aGVpciBldmVudCAoaWYgaXQgb2NjdXJyZWQpLCB0aGV5IGFyZSBjb25zaWRlcmVkIHRvIGJlIGF0IHJpc2sgb2YgdGhlIGV2ZW50IGhhcHBlbmluZyBhdCB0aGF0IHRpbWUuICANCmBgYHtyfQ0KcmFua3N1YjwtIHJhbmt5cnMgJT4lDQogIGZpbHRlcih0aW1lPD1kdXIpDQpgYGANCg0KIC0gYGZpbHRlcih0aW1lIDw9IGR1cilgIGZpbHRlcnMgdGhlIGByYW5reXJzYCBkYXRhIGZyYW1lIHRvIGtlZXAgb25seSB0aGUgcm93cyB3aGVyZSB0aGUgdGltZSBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gdGhlIGBkdXJgIHZhcmlhYmxlLiBUaGUgYGR1cmAgdmFyaWFibGUgcmVwcmVzZW50cyB0aGUgdGltZSB1bnRpbCB0aGUgZXZlbnQgKHByb21vdGlvbikgb2NjdXJyZWQgZm9yIHRob3NlIHdobyB3ZXJlIHByb21vdGVkLCBvciB0aGUgdG90YWwgb2JzZXJ2YXRpb24gdGltZSBmb3IgdGhvc2Ugd2hvIHdlcmUgbm90LiBCeSBmaWx0ZXJpbmcgdGhpcyB3YXksIHdlIGFyZSBvbmx5IGNvbnNpZGVyaW5nIHRoZSBwZXJzb24teWVhcnMgYmVmb3JlIG9yIGF0IHRoZSB0aW1lIG9mIHByb21vdGlvbi4gIA0KIA0KIyMgUmVjb2RlIGV2ZW50IHZhcmlhYmxlIHRvIGJlIDAgYmVmb3JlIGV2ZW50IG9jY3VycmVuY2UgIA0KSW4gZGlzY3JldGUtdGltZSBzdXJ2aXZhbCBhbmFseXNpcywgd2UgbW9kZWwgdGhlIHByb2JhYmlsaXR5IG9mIHRoZSBldmVudCBvY2N1cnJpbmcgaW4gYSBzcGVjaWZpYyB0aW1lIGludGVydmFsLCBnaXZlbiB0aGF0IGl0IGhhcyBub3Qgb2NjdXJyZWQgYmVmb3JlLiBPdXIgcHJvbW8gdmFyaWFibGUgY2FwdHVyZXMgdGhpczogZm9yIGVhY2ggcGVyc29uLXllYXIsIGl0J3MgMSBpZiB0aGUgcHJvbW90aW9uIGhhcHBlbmVkIGF0IHRoYXQgZXhhY3QgdGltZSwgYW5kIDAgb3RoZXJ3aXNlLiAgDQoNCmBgYHtyfQ0KcmFua3N1YiA8LSByYW5rc3ViICU+JQ0KICBtdXRhdGUocHJvbW8gPSBpZl9lbHNlKHRpbWUgPT0gZHVyLCBldmVudCwgMCkpDQpgYGANCg0KIC0gYG11dGF0ZShwcm9tbyA9IGlmX2Vsc2UodGltZSA9PSBkdXIsIGV2ZW50LCAwKSlgIGNyZWF0ZXMgYSBuZXcgYmluYXJ5IG91dGNvbWUgdmFyaWFibGUgY2FsbGVkIGBwcm9tb2AuICANCiAgIC0gYGlmX2Vsc2UoY29uZGl0aW9uLCB2YWx1ZV9pZl90cnVlLCB2YWx1ZV9pZl9mYWxzZSlgIGlzIGEgY29uZGl0aW9uYWwgZnVuY3Rpb24uICANCiAgIC0gYHRpbWUgPT0gZHVyYCBpcyB0aGUgY29uZGl0aW9uIGNoZWNrZWQgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdGhlIGN1cnJlbnQgdGltZSBwb2ludCAoYHRpbWVgKSBpcyBlcXVhbCB0byB0aGUgdGltZSBvZiB0aGUgZXZlbnQgKGBkdXJgKS4gIA0KICAgLSBgZXZlbnRgOiBJZiB0aGUgY29uZGl0aW9uIGlzIHRydWUgKGkuZS4sIHdlIGFyZSBhdCB0aGUgdGltZSBvZiBwcm9tb3Rpb24pLCB0aGUgdmFsdWUgb2YgYHByb21vYCBpcyBzZXQgdG8gdGhlIHZhbHVlIG9mIHRoZSBgZXZlbnRgIHZhcmlhYmxlICh3aGljaCBpcyAxIGZvciBwcm9tb3Rpb24gYW5kIDAgb3RoZXJ3aXNlKS4gIA0KICAgLSBJZiB0aGUgY29uZGl0aW9uIGlzIGZhbHNlIChpLmUuLCB3ZSBhcmUgYXQgYSB0aW1lIHBvaW50IGJlZm9yZSB0aGUgcHJvbW90aW9uKSwgdGhlIHZhbHVlIG9mIHByb21vIGlzIHNldCB0byBgMGAsIGluZGljYXRpbmcgdGhhdCB0aGUgZXZlbnQgZGlkIG5vdCBvY2N1ciBhdCB0aGF0IHNwZWNpZmljIHBlcnNvbi15ZWFyLg0KDQojIyBDaGFuZ2UgdGhlIG1pc3NpbmdzIGluIGpvYnRpbWUgdG8gMTEgIA0KVGhlIGNob2ljZSBvZiAxMSBtaWdodCBiZSBiYXNlZCBvbiB0aGUgY29udGV4dCBvZiB0aGUgZGF0YSAoZS5nLiwgdGhlIG1heGltdW0gb2JzZXJ2ZWQgam9iIHRpbWUgcGx1cyBvbmUuIA0KYGBge3J9DQpyYW5rc3ViIDwtIHJhbmtzdWIgJT4lDQogIG11dGF0ZShqb2J0aW1lID0gaWZfZWxzZShpcy5uYShqb2J0aW1lKSwgMTEsICBqb2J0aW1lKSkNCmBgYA0KDQogLSBgbXV0YXRlKGpvYnRpbWUgPSBpZl9lbHNlKGlzLm5hKGpvYnRpbWUpLCAxMSwgam9idGltZSkpYDoNCiAgIC0gYGlzLm5hKGpvYnRpbWUpYCBjaGVja3MgZm9yIG1pc3NpbmcgdmFsdWVzIChyZXByZXNlbnRlZCBhcyBgTkFgKSBpbiB0aGUgYGpvYnRpbWVgIGNvbHVtbi4gIA0KICAgLSBJZiBhIHZhbHVlIGlzIG1pc3NpbmcgKFRSVUUpLCBpdCdzIHJlcGxhY2VkIHdpdGggMTEuICANCiAgIC0gSWYgYSB2YWx1ZSBpcyBub3QgbWlzc2luZyAoRkFMU0UpLCBpdCByZW1haW5zIGl0cyBvcmlnaW5hbCB2YWx1ZS4NCg0KIyMgRGVmaW5lIGpvYnByZXMsIGEgdGltZS12YXJ5aW5nIGNvdmlhcmlhdGUNClRoaXMgZGVtb25zdHJhdGVzIGhvdyB0byBoYW5kbGUgdGltZS12YXJ5aW5nIGNvdmFyaWF0ZXMgaW4gZGlzY3JldGUtdGltZSBzdXJ2aXZhbCBhbmFseXNpcy4gVGhlIHZhbHVlIG9mIGBqb2JwcmVzYCBjYW4gY2hhbmdlIGZvciBhbiBpbmRpdmlkdWFsIGFzIHRpbWUgcHJvZ3Jlc3NlcywgYW5kIG91ciBkYXRhIHN0cnVjdHVyZSBpbiBsb25nIGZvcm1hdCBhbGxvd3MgdXMgdG8gY2FwdHVyZSB0aGVzZSBjaGFuZ2VzLg0KDQpgYGB7cn0NCnJhbmtzdWIgPC0gcmFua3N1YiAlPiUNCiAgbXV0YXRlKGpvYnByZXMgPSBpZl9lbHNlKHRpbWU8am9idGltZSxwcmVzdDEsIHByZXN0MikpDQpgYGANCiAtIGBtdXRhdGUoam9icHJlcyA9IGlmX2Vsc2UodGltZSA8IGpvYnRpbWUsIHByZXN0MSwgcHJlc3QyKSlgIGNyZWF0ZXMgYSBuZXcgdmFyaWFibGUgYGpvYnByZXNgLiAgDQogICAtIGB0aW1lIDwgam9idGltZWAgaXMgdGhlIGNvbmRpdGlvbiBjaGVja2VkIHdoZXJlIHRoZSBjdXJyZW50IHRpbWUgcG9pbnQgKHRpbWUpIGlzIGxlc3MgdGhhbiB0aGUgam9idGltZS4gIA0KICAgLSBJZiB0aGUgY29uZGl0aW9uIGlzIHRydWUgKGkuZS4sIGJlZm9yZSBgam9idGltZWApLCB0aGUgdmFsdWUgb2YgYGpvYnByZXNgIGlzIHRha2VuIGZyb20gdGhlIGBwcmVzdDFgIHZhcmlhYmxlIChsaWtlbHkgcHJlc3RpZ2UgYXQgYW4gZWFybGllciBqb2IpLiAgDQogICAtIElmIHRoZSBjb25kaXRpb24gaXMgZmFsc2UgKGkuZS4sIGF0IG9yIGFmdGVyIGBqb2J0aW1lYCksIHRoZSB2YWx1ZSBvZiBgam9icHJlc2AgaXMgdGFrZW4gZnJvbSB0aGUgYHByZXN0MmAgdmFyaWFibGUgKGxpa2VseSBwcmVzdGlnZSBhdCBhIGxhdGVyIG9yIGN1cnJlbnQgam9iKS4gIA0KDQojIyBGaXQgZGlzY3JldGUgdGltZSBsb2dpdCAgDQpUaGlzIGBnbG0oKWAgY2FsbCBmaXRzIGEgZGlzY3JldGUtdGltZSBsb2dpdCBtb2RlbCB3aGVyZSB0aGUgb3V0Y29tZSBpcyB0aGUgbG9nLW9kZHMgb2YgcHJvbW90aW9uIGF0IGEgc3BlY2lmaWMgdGltZSBwb2ludC4gVGhlIGNvZWZmaWNpZW50cyBmcm9tIHRoaXMgbW9kZWwgd2lsbCB0ZWxsIHVzIGhvdyBlYWNoIHByZWRpY3RvciB2YXJpYWJsZSBpcyBhc3NvY2lhdGVkIHdpdGggdGhpcyBsb2ctb2RkcywgaG9sZGluZyBvdGhlciB2YXJpYWJsZXMgY29uc3RhbnQuIFdlIHdpbGwgbmVlZCB0byBleHBvbmVudGlhdGUgdGhlc2UgY29lZmZpY2llbnRzIHRvIGludGVycHJldCB0aGVtIGFzIG9kZHMgcmF0aW9zLg0KDQpgYGB7cn0NCm15Zml0PC1nbG0ocHJvbW9+dW5kZ3JhZCArIHBoZG1lZCArIHBoZHByZXN0ICsgYXJ0ICsgY2l0ICsgam9icHJlcyArIHRpbWUgKyBJKHRpbWVeMiksIGZhbWlseT1iaW5vbWlhbCxkYXRhPXJhbmtzdWIpDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoZ3RzdW1tYXJ5KQ0KDQojIENyZWF0ZSBhIHN1bW1hcnkgdGFibGUgb2YgdGhlIG1vZGVsIHJlc3VsdHMNCm1vZGVsX3RhYmxlIDwtIHRibF9yZWdyZXNzaW9uKA0KICBteWZpdCwNCiAgZXhwb25lbnRpYXRlID0gVFJVRSwgIyBEaXNwbGF5IG9kZHMgcmF0aW9zIGluc3RlYWQgb2YgbG9nLW9kZHMNCiAgcHZhbHVlX2Z1biA9IH5zdHlsZV9wdmFsdWUoLngsIGRpZ2l0cyA9IDMpLCAjIEZvcm1hdCBwLXZhbHVlcw0KICBsYWJlbCA9IGxpc3QoDQogICAgInVuZGdyYWQiIH4gIlVuZGVyZ3JhZHVhdGUgSW5zdGl0dXRpb24gUHJlc3RpZ2UiLA0KICAgICJwaGRtZWQiIH4gIk1lZGljYWwgUGhEIiwNCiAgICAicGhkcHJlc3QiIH4gIlBoRCBQcm9ncmFtIFByZXN0aWdlIiwNCiAgICAiYXJ0IiB+ICJOdW1iZXIgb2YgQXJ0aWNsZXMiLA0KICAgICJjaXQiIH4gIk51bWJlciBvZiBDaXRhdGlvbnMiLA0KICAgICJqb2JwcmVzIiB+ICJKb2IgUHJlc3RpZ2UiLA0KICAgICJ0aW1lIiB+ICJUaW1lIChZZWFycykiLA0KICAgICJJKHRpbWVeMikiIH4gIlRpbWUgU3F1YXJlZCIgIyBVc2UgdGhlIGV4YWN0IHN0cmluZyAiSSh0aW1lXjIpIg0KICApICMgQ3VzdG9tIGxhYmVscyBmb3IgdmFyaWFibGVzDQopDQoNCiMgUHJpbnQgdGhlIHRhYmxlIHRvIHRoZSBjb25zb2xlDQptb2RlbF90YWJsZQ0KYGBgDQoNCmBgYHtyfQ0KIyBZb3UgY2FuIGZ1cnRoZXIgY3VzdG9taXplIHRoZSB0YWJsZSwgZm9yIGV4YW1wbGUsIGFkZGluZyBhIHRpdGxlIGFuZCBjYXB0aW9uDQptb2RlbF90YWJsZSA8LSBtb2RlbF90YWJsZSAlPiUNCiAgYXNfZ3QoKSAlPiUgIyBDb252ZXJ0IHRvIGEgZ3QgdGFibGUgZm9yIG1vcmUgYWR2YW5jZWQgZm9ybWF0dGluZw0KICBndDo6dGFiX2hlYWRlcigNCiAgICB0aXRsZSA9ICJEaXNjcmV0ZS1UaW1lIExvZ2l0IE1vZGVsIFJlc3VsdHMiLA0KICAgIHN1YnRpdGxlID0gIlByZWRpY3RpbmcgUHJvYmFiaWxpdHkgb2YgUHJvbW90aW9uIg0KICApICU+JQ0KICBndDo6dGFiX2NhcHRpb24oIk9kZHMgcmF0aW9zIGFuZCBwLXZhbHVlcyBhcmUgcmVwb3J0ZWQuIikNCg0KIyBQcmludCB0aGUgY3VzdG9taXplZCB0YWJsZQ0KbW9kZWxfdGFibGU=