This guide implements the preprocessing of an example dataset gathered with m-Path and loaded with its companion R package mpathr. m-Path is a platform that allows researchers to create and deploy Experience Sampling Method (ESM) studies. ESM studies are a type of longitudinal study where participants are prompted to answer questions about their emotions, activities, and context in real-time.

The dataset consists of the resulting data from an ESM experiment, from 20 participants collected over the course of 10 days. Participants completed the following questionnaires:

  • Intake questionnaire: where participants consented to participate and filled the Neuroticism subscale of the Big Five Inventory (BFI) and the Satisfaction with Life Scale (SWLS).
  • Main questionnaire (administered 10 times per day): Participants answered questions about their emotions and activities.
  • Evening questionnaire (every evening): where participants rated their emotions throughout the day.

The dataset is available through the mpath_example() function in mpathr but can also be downloaded separately from Github here.

This guide builds upon the framework, R tutorials, and templates for preprocessing ESM data developed by Revol et al. (2024). These resources are detailed in the associated paper and the ESM Preprocessing Gallery website. When using this work, please cite the following paper:

  • Revol, J., Carlier, C., Lafit, G., Verhees, M., Sels, L., & Ceulemans, E. (2024). Preprocessing Experience-Sampling-Method Data: A Step-by-Step Framework, Tutorial Website, R Package, and Reporting Templates. Advances in Methods and Practices in Psychological Science, 7(4), 1–18. https://doi.org/10.1177/25152459241256609.

Set up

We will start by loading the packages that we will use in this analysis. Since this guide requires the installation of a good number of packages, the following code automatically installs packages that are not already installed. Packages are loaded whenever these are needed in the guide.

required_packages <- c(
  "mpathr", "esmtools", "skimr", "dplyr", "tidyr", "visdat", "lubridate", "ggplot2", 
  "gglalt", "scales", "lme4", "performance", "car", "modelbased", "rsq"
)

# checks which of the packages are installed and installs the ones that are not
install.packages(setdiff(required_packages, rownames(installed.packages())))

# create m-Path palettes (for plotting):
ggplot2::theme_set(ggplot2::theme_bw())

# Colors for the plots in this guide
mpath_palette <- c("#2196F3", "#F33920", "#F4A222")
dark_mpath_palette <- c("#087DDA", "#DA2006", "#DB8908")

Step 1: Importing data and preliminary preprocessing

In this step, we will import the data and perform some preliminary preprocessing. We first take a look at the data, and then we clean it by renaming columns and making composite variables. We then check for duplicated and missing variables, and finally reformat the time stamps and create new time variables.

Importing

To read the basic.csv as exported from m-Path, we can use the read_mpath() function, that takes as arguments the path to the data (file) and the path to the meta data (meta_data), and returns the data as a data frame. The metadata contains information about how the data should be read, this information is used by the read_mpath() function to get the correct data types. In this guide, we will use the example data that comes with the mpathr package, which can be found by calling mpath_example().

library(mpathr)

data <- read_mpath(
  file = mpath_example("example_basic.csv"), # path to the data 
  meta_data = mpath_example("example_meta.csv") # path to the metadata
) 

Note: saving m-Path data

The resulting data frame will contain columns with lists, which can be problematic when saving the data. To save the data, we suggest the following two options:

If you want to save the data as a comma-separated values (CSV) file to use it in another program, use write_mpath(). This function will collapse most list columns to a single string and parses all character columns to JSON strings, essentially reversing the operations performed by read_mpath(). Note that this does not mean that data can be read back using read_mpath(), because the data may have been modified and thus no longer be in line with the meta data.

write_mpath(
  x = data,
  file = "data.csv"
)

Otherwise, if the data will be used exclusively in R, we suggest saving it as an R object (.RData or .RDS):

# As an .RData file. When using `load()`, note that the data will be stored in the `data`
# object in the global environment.
save(
  data, 
  file = "data.RData"
)

# As an RDS file.
saveRDS(
  data, 
  file = "data.RDS"
)

First glimpse

We can first check the data to get a better idea of what we are working with.

Meta-info of the ESM dataset. The dataInfo() function from the esmtools package provides some useful information, such as the number of columns and rows, the number of participants, the number of observations per participant, and the number of missing values.

library(esmtools)

dataInfo(
  file_path = mpath_example("example_basic.csv"),
  read_fun = read.csv2,
  idvar = "connectionId", # specify the participant variable
  timevar = "timeStampSent" # specify the time variable
)
#> Path : C:/Workdir/MyApps/R-Library/4.0/mpathr/extdata/example_basic.csv 
#> Extension : csv 
#> Size : 573.3 Kb 
#> Creation time : 2024-10-16 16:55:30.683758 
#> Update time : 2024-10-16 16:55:30.684759 
#> ncol : 100 
#> nrow : 2221 
#> Number participants : 20 
#> Average number obs : 111.05 
#> Period : from 2024-04-15 18:21:41 to 2024-06-04 00:10:01 
#> Variables : connectionId, legacyCode, code, alias, initials, accountCode, scheduledBeepId, sentBeepId, reminderForOriginalSentBeepId, questionListName, questionListLabel, fromProtocolName, timeStampScheduled, timeStampSent, timeStampStart, timeStampStop, originalTimeStampSent, timeZoneOffset, deltaUTC, consent_yesno_yesno, gender_multipleChoice_index, gender_multipleChoice_string, gender_multipleChoice_likert, age_open, SWLS_intro_basic, SWLS_1_multipleChoice_index, SWLS_1_multipleChoice_string, SWLS_1_multipleChoice_likert, SWLS_2_multipleChoice_index, SWLS_2_multipleChoice_string, SWLS_2_multipleChoice_likert, SWLS_3_multipleChoice_index, SWLS_3_multipleChoice_string, SWLS_3_multipleChoice_likert, SWLS_4_multipleChoice_index, SWLS_4_multipleChoice_string, SWLS_4_multipleChoice_likert, SWLS_5_multipleChoice_index, SWLS_5_multipleChoice_string, SWLS_5_multipleChoice_likert, BFI_neuroticism_intro_basic, BFI_neuroticism_4_multipleChoice_index, BFI_neuroticism_4_multipleChoice_string, BFI_neuroticism_4_multipleChoice_likert, BFI_neuroticism_9R_multipleChoice_index, BFI_neuroticism_9R_multipleChoice_string, BFI_neuroticism_9R_multipleChoice_likert, BFI_neuroticism_14_multipleChoice_index, BFI_neuroticism_14_multipleChoice_string, BFI_neuroticism_14_multipleChoice_likert, BFI_neuroticism_19_multipleChoice_index, BFI_neuroticism_19_multipleChoice_string, BFI_neuroticism_19_multipleChoice_likert, BFI_neuroticism_24R_multipleChoice_index, BFI_neuroticism_24R_multipleChoice_string, BFI_neuroticism_24R_multipleChoice_likert, BFI_neuroticism_29_multipleChoice_index, BFI_neuroticism_29_multipleChoice_string, BFI_neuroticism_29_multipleChoice_likert, BFI_neuroticism_34R_multipleChoice_index, BFI_neuroticism_34R_multipleChoice_string, BFI_neuroticism_34R_multipleChoice_likert, BFI_neuroticism_39_multipleChoice_index, BFI_neuroticism_39_multipleChoice_string, BFI_neuroticism_39_multipleChoice_likert, intake_outro_basic, slider_happy_sliderNeutralPos, slider_sad_sliderNegPos, slider_angry_sliderNegPos, slider_relaxed_sliderNegPos, slider_anxious_sliderNegPos, slider_energetic_sliderNegPos, slider_tired_sliderNegPos, context_location_multipleChoice_index, context_location_multipleChoice_string, context_location_multipleChoice_likert, context_company_multipleChoice_index, context_company_multipleChoice_string, context_company_multipleChoice_likert, context_activity_multipleChoice_index, context_activity_multipleChoice_string, context_activity_multipleChoice_likert, step_count_steps, main_outro_basic, evening_questionnaire_warning_basic, evening_slider_happy_sliderNeutralPos, evening_slider_sad_sliderNegPos, evening_slider_angry_sliderNegPos, evening_slider_relaxed_sliderNegPos, evening_slider_anxious_sliderNegPos, evening_slider_energetic_sliderNegPos, evening_slider_tired_sliderNegPos, evening_yesno_stressful_yesno, evening_yesno_positive_yesno, positive_description_open, evening_context_activity_multipleChoice_index, evening_context_activity_multipleChoice_string, evening_context_activity_multipleChoice_likert, evening_outro_basic, stressful_description_open

We can see that we have 20 participants, and we have 111.05 observations per participant.

We can also print the data and use the skim() function from the skimr package to get a summary of the data.

library(skimr)

head(data) # prints first rows of df
#> # A tibble: 6 × 100
#>   connectionId legacyCode  code       alias initials accountCode scheduledBeepId sentBeepId
#>          <int> <chr>       <chr>      <chr> <chr>    <chr>                 <int>      <int>
#> 1       234609 !9v48@jp7a7 !byyo kjyt abc   Ver      jp7a7                    -1   19355815
#> 2       234609 !9v48@jp7a7 !byyo kjyt abc   Ver      jp7a7              28626776   19369681
#> 3       234609 !9v48@jp7a7 !byyo kjyt abc   Ver      jp7a7              28626777   19370288
#> 4       234609 !9v48@jp7a7 !byyo kjyt abc   Ver      jp7a7              28626781   19375253
#> 5       234609 !9v48@jp7a7 !byyo kjyt abc   Ver      jp7a7              28626782   19377280
#> 6       234609 !9v48@jp7a7 !byyo kjyt abc   Ver      jp7a7              28626784   19380381
#> # ℹ 92 more variables: reminderForOriginalSentBeepId <int>, questionListName <chr>,
#> #   questionListLabel <chr>, fromProtocolName <chr>, timeStampScheduled <int>, timeStampSent <int>,
#> #   timeStampStart <int>, timeStampStop <int>, originalTimeStampSent <int>, timeZoneOffset <int>,
#> #   deltaUTC <dbl>, consent_yesno_yesno <int>, gender_multipleChoice_index <int>,
#> #   gender_multipleChoice_string <chr>, gender_multipleChoice_likert <int>, age_open <chr>,
#> #   SWLS_intro_basic <dbl>, SWLS_1_multipleChoice_index <int>, SWLS_1_multipleChoice_string <chr>,
#> #   SWLS_1_multipleChoice_likert <int>, SWLS_2_multipleChoice_index <int>, …
print(skim(data)) # prints summary of df
#> ── Data Summary ────────────────────────
#>                            Values
#> Name                       data  
#> Number of rows             2221  
#> Number of columns          100   
#> _______________________          
#> Column type frequency:           
#>   character                28    
#>   list                     3     
#>   numeric                  69    
#> ________________________         
#> Group variables            None  
#> 
#> ── Variable type: character ────────────────────────────────────────────────────────────────────────
#>    skim_variable                n_missing complete_rate min max empty n_unique whitespace
#>  1 legacyCode                         111       0.950    11  11     0       19          0
#>  2 code                               111       0.950    10  10     0       19          0
#>  3 alias                                0       1         2  12     0       20          0
#>  4 initials                             0       1         2   3     0       20          0
#>  5 accountCode                          0       1         5   5     0        1          0
#>  6 questionListName                     0       1        18  32     0        3          0
#>  7 questionListLabel                 2221       0        NA  NA     0        0          0
#>  8 fromProtocolName                    20       0.991    19  19     0        1          0
#>  9 gender_multipleChoice_string      2201       0.00900   4   6     0        2          0
#> 10 age_open                          2201       0.00900   2   2     0       13          0
#> # ℹ 18 more rows
#> 
#> ── Variable type: list ─────────────────────────────────────────────────────────────────────────────
#>   skim_variable                     n_missing complete_rate n_unique min_length max_length
#> 1 evening_context_activity_multipl…      2079        0.0639       71          1          6
#> 2 evening_context_activity_multipl…      2079        0.0639       71          1          6
#> 3 evening_context_activity_multipl…      2221        0             0          1          1
#> 
#> ── Variable type: numeric ──────────────────────────────────────────────────────────────────────────
#>    skim_variable n_missing complete_rate   mean     sd      p0    p25    p50    p75   p100
#>  1 connectionId          0         1     2.36e5 1.73e3  2.34e5 2.35e5 2.35e5 2.37e5 2.40e5
#>  2 scheduledBee…         0         1     2.85e7 2.73e6 -1   e0 2.86e7 2.87e7 2.89e7 2.92e7
#>  3 sentBeepId            0         1     1.96e7 1.58e5  1.93e7 1.94e7 1.95e7 1.97e7 1.99e7
#>  4 reminderForO…         0         1     0      0       0      0      0      0      0     
#>  5 timeStampSch…         0         1     1.70e9 1.62e8  0      1.71e9 1.71e9 1.72e9 1.72e9
#>  6 timeStampSent         0         1     1.71e9 1.04e6  1.71e9 1.71e9 1.71e9 1.72e9 1.72e9
#>  7 timeStampSta…       808         0.636 1.71e9 1.00e6  1.71e9 1.71e9 1.71e9 1.72e9 1.72e9
#>  8 timeStampStop       808         0.636 1.71e9 1.00e6  1.71e9 1.71e9 1.71e9 1.72e9 1.72e9
#>  9 originalTime…       808         0.636 0      0       0      0      0      0      0     
#> 10 timeZoneOffs…         0         1     7.32e3 6.38e2  7.2 e3 7.2 e3 7.2 e3 7.2 e3 1.08e4
#> # ℹ 59 more rows
#> # ℹ 1 more variable: hist <chr>

From the output of the skim function, we can see that we don”t have any missing values in connectionId (our participant identifier variable), and also in the time variable timeStampSent. But we have a lot of missing values in other variables. This is because each row corresponds to one beep, and since the data consists of three questionnaires, not all questions are answered in each beep.

Initial cleaning and column renaming

Before continuing with the pre-processing, we will do some initial cleaning and make the columns more readable.
colnames(data) # prints the column names
#>   [1] "connectionId"                                  
#>   [2] "legacyCode"                                    
#>   [3] "code"                                          
#>   [4] "alias"                                         
#>   [5] "initials"                                      
#>   [6] "accountCode"                                   
#>   [7] "scheduledBeepId"                               
#>   [8] "sentBeepId"                                    
#>   [9] "reminderForOriginalSentBeepId"                 
#>  [10] "questionListName"                              
#>  [11] "questionListLabel"                             
#>  [12] "fromProtocolName"                              
#>  [13] "timeStampScheduled"                            
#>  [14] "timeStampSent"                                 
#>  [15] "timeStampStart"                                
#>  [16] "timeStampStop"                                 
#>  [17] "originalTimeStampSent"                         
#>  [18] "timeZoneOffset"                                
#>  [19] "deltaUTC"                                      
#>  [20] "consent_yesno_yesno"                           
#>  [21] "gender_multipleChoice_index"                   
#>  [22] "gender_multipleChoice_string"                  
#>  [23] "gender_multipleChoice_likert"                  
#>  [24] "age_open"                                      
#>  [25] "SWLS_intro_basic"                              
#>  [26] "SWLS_1_multipleChoice_index"                   
#>  [27] "SWLS_1_multipleChoice_string"                  
#>  [28] "SWLS_1_multipleChoice_likert"                  
#>  [29] "SWLS_2_multipleChoice_index"                   
#>  [30] "SWLS_2_multipleChoice_string"                  
#>  [31] "SWLS_2_multipleChoice_likert"                  
#>  [32] "SWLS_3_multipleChoice_index"                   
#>  [33] "SWLS_3_multipleChoice_string"                  
#>  [34] "SWLS_3_multipleChoice_likert"                  
#>  [35] "SWLS_4_multipleChoice_index"                   
#>  [36] "SWLS_4_multipleChoice_string"                  
#>  [37] "SWLS_4_multipleChoice_likert"                  
#>  [38] "SWLS_5_multipleChoice_index"                   
#>  [39] "SWLS_5_multipleChoice_string"                  
#>  [40] "SWLS_5_multipleChoice_likert"                  
#>  [41] "BFI_neuroticism_intro_basic"                   
#>  [42] "BFI_neuroticism_4_multipleChoice_index"        
#>  [43] "BFI_neuroticism_4_multipleChoice_string"       
#>  [44] "BFI_neuroticism_4_multipleChoice_likert"       
#>  [45] "BFI_neuroticism_9R_multipleChoice_index"       
#>  [46] "BFI_neuroticism_9R_multipleChoice_string"      
#>  [47] "BFI_neuroticism_9R_multipleChoice_likert"      
#>  [48] "BFI_neuroticism_14_multipleChoice_index"       
#>  [49] "BFI_neuroticism_14_multipleChoice_string"      
#>  [50] "BFI_neuroticism_14_multipleChoice_likert"      
#>  [51] "BFI_neuroticism_19_multipleChoice_index"       
#>  [52] "BFI_neuroticism_19_multipleChoice_string"      
#>  [53] "BFI_neuroticism_19_multipleChoice_likert"      
#>  [54] "BFI_neuroticism_24R_multipleChoice_index"      
#>  [55] "BFI_neuroticism_24R_multipleChoice_string"     
#>  [56] "BFI_neuroticism_24R_multipleChoice_likert"     
#>  [57] "BFI_neuroticism_29_multipleChoice_index"       
#>  [58] "BFI_neuroticism_29_multipleChoice_string"      
#>  [59] "BFI_neuroticism_29_multipleChoice_likert"      
#>  [60] "BFI_neuroticism_34R_multipleChoice_index"      
#>  [61] "BFI_neuroticism_34R_multipleChoice_string"     
#>  [62] "BFI_neuroticism_34R_multipleChoice_likert"     
#>  [63] "BFI_neuroticism_39_multipleChoice_index"       
#>  [64] "BFI_neuroticism_39_multipleChoice_string"      
#>  [65] "BFI_neuroticism_39_multipleChoice_likert"      
#>  [66] "intake_outro_basic"                            
#>  [67] "slider_happy_sliderNeutralPos"                 
#>  [68] "slider_sad_sliderNegPos"                       
#>  [69] "slider_angry_sliderNegPos"                     
#>  [70] "slider_relaxed_sliderNegPos"                   
#>  [71] "slider_anxious_sliderNegPos"                   
#>  [72] "slider_energetic_sliderNegPos"                 
#>  [73] "slider_tired_sliderNegPos"                     
#>  [74] "context_location_multipleChoice_index"         
#>  [75] "context_location_multipleChoice_string"        
#>  [76] "context_location_multipleChoice_likert"        
#>  [77] "context_company_multipleChoice_index"          
#>  [78] "context_company_multipleChoice_string"         
#>  [79] "context_company_multipleChoice_likert"         
#>  [80] "context_activity_multipleChoice_index"         
#>  [81] "context_activity_multipleChoice_string"        
#>  [82] "context_activity_multipleChoice_likert"        
#>  [83] "step_count_steps"                              
#>  [84] "main_outro_basic"                              
#>  [85] "evening_questionnaire_warning_basic"           
#>  [86] "evening_slider_happy_sliderNeutralPos"         
#>  [87] "evening_slider_sad_sliderNegPos"               
#>  [88] "evening_slider_angry_sliderNegPos"             
#>  [89] "evening_slider_relaxed_sliderNegPos"           
#>  [90] "evening_slider_anxious_sliderNegPos"           
#>  [91] "evening_slider_energetic_sliderNegPos"         
#>  [92] "evening_slider_tired_sliderNegPos"             
#>  [93] "evening_yesno_stressful_yesno"                 
#>  [94] "evening_yesno_positive_yesno"                  
#>  [95] "positive_description_open"                     
#>  [96] "evening_context_activity_multipleChoice_index" 
#>  [97] "evening_context_activity_multipleChoice_string"
#>  [98] "evening_context_activity_multipleChoice_likert"
#>  [99] "evening_outro_basic"                           
#> [100] "stressful_description_open"

There are some columns that we will not need for our analysis (but may contain useful information in other cases). We can already remove them by using the select() function in the dplyr package. If we use the - or ! sign before listing the columns, we select everything except those columns:

library(dplyr)

# We provide the function with a vector with the column names we want to remove
data <- data |> 
  select(-c(
    "legacyCode", "alias", "initials", "scheduledBeepId", "sentBeepId", "accountCode", 
    "reminderForOriginalSentBeepId", "originalTimeStampSent", "questionListLabel", 
    "timeZoneOffset", "fromProtocolName", "SWLS_intro_basic", 
    "BFI_neuroticism_intro_basic", "intake_outro_basic", "main_outro_basic",
    "evening_questionnaire_warning_basic", "evening_outro_basic"
  )) 

Note: in this case, we are removing the column originalTimeStampSent because in our experiment, we did not have reminders. In case of reminders, the originalTimeStampSent contains the time stamp for the first notification and timeStampSent contains the time stamp for the reminder.

Once we have removed some columns, we can rename the rest. Since we are working with 83 columns, changing each column name manually would be very time-consuming.

To avoid doing this, we may want to check the column names and see if there is a pattern that we can use to make our task easier. In this case, since our questionnaire was implemented in m-Path, the column names consist of the label we gave in questionnaire creation, as well as a suffix indicated the type of question (For example: age_open has the suffix _open). Knowing this, we can remove some of these suffixes in all the columns that have them.

An example of this is the Multiple choice questions (that end in _multipleChoice). We can remove these using the gsub() function. In this function, we supply the column names, specify the pattern we want to replace (_multipleChoice), and the replacement ("", an empty string).

patterns <- c(
  "_yesno$", # removes _yesno, but only if it is at the end of the string (using $) 
  "_multipleChoice", # Remove _multipleChoice suffix
  "_sliderN.*", # Remove _slider suffixes and everything after it
  "_open", # Remove _open
  "context_" # Remove context_ 
)

# Merge them into one string that we can use a regular expression
patterns <- paste0("(", paste(patterns, collapse = "|"), ")")

colnames(data) <- gsub(
  pattern = patterns, 
  replacement = "",
  x = colnames(data)
)

Warning: cleaning column names in this way may result in duplicated column names For example: if there was a column called slider_happy_sliderNegPos, we would end up with two columns named slider_happy, because of the column slider_happy_sliderNeutralPos. This can be a problem, so it is important to be aware of this and maybe check for duplicated column names after cleaning them.

# checking for duplicated column names
anyDuplicated(names(data))
#> [1] 0

We have no duplicated column names as a result of the previous renaming.


Now, we have removed some suffixes and our column names are less cluttered. We can rename the rest of the columns manually with rename(). Note that this is not necessary, but we will use these renaming standards in this guide.

data <- data |> 
  rename(
    participant = "connectionId", # renaming connectionId to participant
    gender = "gender_index",
    questionnaire = "questionListName",
    scheduled = "timeStampScheduled",
    sent = "timeStampSent",
    start = "timeStampStart",
    stop = "timeStampStop",
    step_count = "step_count_steps",
    phone_server_offset = "deltaUTC",
    evening_stressful = "evening_yesno_stressful",
    evening_positive = "evening_yesno_positive"
  )

Replace participant values with a participant number

Our participant identifier is the column participant, which contains IDs such as 234609. We can replace these IDs with a participant number that goes from 1 to the number of participants.

But first, we should check that there are no NAs in this column:
# first check that there are no missing values in the participant variable
anyNA(data$participant)
#> [1] FALSE

There are no NAs in the participant variable, so we can safely replace it with a participant number. We can use the function match() to replace the participant alias with its index in unique(data$participant). This will yield us with participant numbers that go from 1 to the number of participants (which is length(unique(data$participant))).

# print unique values in data$participant before match
unique(data$participant) 
#>  [1] 234609 235458 234587 234086 235790 234639 234011 234455 234579 234859 234860 234889 234980
#> [14] 235052 237139 237953 238000 238550 238707 239674
# matches the values in data$participant with the unique values in data$participant and
# returns the index of the match
data$participant <- match(data$participant, unique(data$participant)) 

# print unique values in data$participant after match
unique(data$participant) 
#>  [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

Making composite variables

After having done some initial cleaning, we can create composite variables. In our study, participants completed a main_questionnaire, an evening_questionnaire and a Consent and intake questionnaire. In this last one, participants provided information that does not change over time.

# to check which different questionnaires are part of the dataset:
unique(data$questionnaire) 
#> [1] "Consent and intake questionnaire" "main_questionnaire"              
#> [3] "evening_questionnaire"

In the intake questionnaire, participants were asked about:

  1. Their age and gender
  2. Their level of Neuroticism, according to the Neuroticism subscale of the Big Five Inventory (BFI)
  3. Satisfaction with Life Scale (SWLS).

We can create composite variables for these 2 scales and add them to the main dataset.

 # Create a data frame with only intake questionnaire rows
intake_data <- filter(data, questionnaire == "Consent and intake questionnaire")

# check that everyone that is in data is also in intake_data -> everyone completed intake survey
setequal(data$participant, intake_data$participant)
#> [1] TRUE

Now that we have a new data frame with only the intake questionnaire rows, we create the composite variable for the SWLS, which is the sum of its 5 items:

# create composite variables
# For life Satisfaction: sum of the 5 items
intake_data <- intake_data |> 
  mutate(life_satisfaction = rowSums(pick( 
    "SWLS_1_likert",
    "SWLS_2_likert",
    "SWLS_3_likert",
    "SWLS_4_likert",
    "SWLS_5_likert"
  )))

# Alternatively, if you have many columns you need to sum, you can best do so using a
# regular expression:
intake_data <- intake_data |> 
  mutate(life_satisfaction = rowSums(pick(matches("SWLS_[1-5]_likert"))))
For the composite variable for neuroticism:, we take the mean of the 8 items. However, the variables named with an “R” after the question number (like "BFI_neuroticism_9R_multipleChoice_likert") need to be reversed.
# For each column that matches our regular expression, We apply the function (`\(x)` is a
# shorthand for `function(x)`) `6 - x`, to reverse the scores of the items. The regular
# expression is as follows: The literal string "BFI_neuroticism_` followed by at least one
# (that"s where the `+` sign is for) number (the `\\d` character) followed by the letter R
# and then the literal string "_likert". This should match all of the reverse-keyed BFI
# items. Optionally, we can reencode the names using a `glue::glue()` expression, where we
# can insert code to run the function `gsub()` to replace the letter "R" with nothing, to
# match the names of the other BFI items.
intake_data <- intake_data |> 
    mutate(across(
        .cols = matches("BFI_neuroticism_\\d+R_likert"),
        .fns = \(x) 6 - x,
        .names = "{gsub('R', '', .col)}"
    )) 

# We can then calculate the mean of the BFI items using a regular expression as before.
# The function `matches()` picks all the columns corresponding to the BFI, but only the 
# reversed ones (it does not pick columns with an R like BFI_neuroticism_9R_likert)
intake_data <- intake_data |> 
  mutate(neuroticism = rowMeans(pick( 
    matches("BFI_neuroticism_[4,9,14,19,24,29,34,39]_likert")
  )))

Now that we created the composite variables for the intake questionnaire, we can join them to the main dataset. This also means that we can remove the rows corresponding to the intake questionnaire from the main dataset, as we have already extracted the information we needed.

# Select only relevant columns for `intake_data`
intake_data <- intake_data |> 
  select(
    "participant", "gender", "gender_string", "age", 
    "life_satisfaction", "neuroticism"
  )

# First, remove the rows corresponding to the intake questionnaire from the main dataset
# as well as the column from the intake questionnaire
data <- data |> 
  filter(questionnaire != "Consent and intake questionnaire") |> 
  select(-"consent_yesno":-"BFI_neuroticism_39_likert") 

# Then, match the intake data with the composite scores back to the main dataset by using
# the participant number as a key
# Also relocate the columns from the intake data to be placed before the ESM columns (but
# after the "default" columns)
data <- data |> 
  left_join(intake_data, by = "participant") |> 
  relocate("gender":"neuroticism", .before = "slider_happy")

Checking for duplicated variables

Following the ESM Preprocessing Gallery guidelines for duplication, we will check for the following three types of duplication:

1. Duplicated rows: two rows that are exactly identical. We do not have any:
# Returns 0 if there are no duplications, or the indices of the duplicated rows otherwise
anyDuplicated(data) 
#> [1] 0
2. Duplicated answers: we want to check whether any observation have exactly the same answers. We do not have any:
# Load the package `tidyr` to use `drop_na()` to remove missing values
library(tidyr)

duplicated_answers <- data |> 
  drop_na(start) |>  # remove rows that correspond to unanswered beeps
  group_by(pick("slider_happy":last_col())) |> # Group by all the items
  filter(n() > 1) # check for duplicated answers in that column

duplicated_answers
#> # A tibble: 0 × 44
#> # ℹ 44 variables: participant <int>, code <chr>, questionnaire <chr>, scheduled <int>, sent <int>,
#> #   start <int>, stop <int>, phone_server_offset <dbl>, gender <int>, gender_string <chr>,
#> #   age <chr>, life_satisfaction <dbl>, neuroticism <dbl>, slider_happy <int>, slider_sad <int>,
#> #   slider_angry <int>, slider_relaxed <int>, slider_anxious <int>, slider_energetic <int>,
#> #   slider_tired <int>, location_index <int>, location_string <chr>, location_likert <int>,
#> #   company_index <int>, company_string <chr>, company_likert <int>, activity_index <int>,
#> #   activity_string <chr>, activity_likert <int>, step_count <int>, evening_slider_happy <int>, …

3. Duplicated time stamps we will check that the same participant does not have duplicated time stamps.

First, we will check for duplicates in the sent variable.

# Different participants can correctly have the same time stamp
# so we will just check that the same participant does not have duplicated time stamps:
duplicated_sent <- data |> 
  group_by(participant, sent) |> 
  filter(n() > 1) 

duplicated_sent
#> # A tibble: 2 × 44
#>   participant code       questionnaire     scheduled   sent  start   stop phone_server_offset gender
#>         <int> <chr>      <chr>                 <int>  <int>  <int>  <int>               <dbl>  <int>
#> 1          11 !csns cnzc evening_question…    1.71e9 1.71e9 1.71e9 1.71e9                   0      1
#> 2          11 !csns cnzc evening_question…    1.71e9 1.71e9 1.71e9 1.71e9                   0      1
#> # ℹ 35 more variables: gender_string <chr>, age <chr>, life_satisfaction <dbl>, neuroticism <dbl>,
#> #   slider_happy <int>, slider_sad <int>, slider_angry <int>, slider_relaxed <int>,
#> #   slider_anxious <int>, slider_energetic <int>, slider_tired <int>, location_index <int>,
#> #   location_string <chr>, location_likert <int>, company_index <int>, company_string <chr>,
#> #   company_likert <int>, activity_index <int>, activity_string <chr>, activity_likert <int>,
#> #   step_count <int>, evening_slider_happy <int>, evening_slider_sad <int>,
#> #   evening_slider_angry <int>, evening_slider_relaxed <int>, evening_slider_anxious <int>, …

It seems that one participant answered the evening questionnaire twice. In our case, this was possible because participants were allowed to fill the evening questionnaire multiple times if they clicked on the “repeat questionnaire” button in the app.

We can just remove the questionnaire that was answered later, as follows:
# For each participant and sent time, take the earliest `start` time (and thereby removing
# later `start` times). This will remove the duplicated time stamp.
data <- data |> 
  group_by(participant, sent) |>
  slice_min(start, n = 1, with_ties = FALSE) |> 
  ungroup()

Now, we can check that there are no duplicates in the start column:

duplicated_start <- data |> 
  drop_na(start) |>  # remove rows that correspond to unanswered beeps
  group_by(participant, start) |> 
  filter(n() > 1)

nrow(duplicated_start) # no duplicated start time stamps!
#> [1] 0

There are no duplicates in start.

First missingness analysis

It”s important to investigate and understand missing values as it can help us understand participants response patterns, and identify data collection errors.

In our case, participants were not able to skip questions, so missing values may also indicate errors in the data collection process. However, we did have branching questions in the evening questionnaire, where participants were asked if they had had a stressful/positive event that day and were prompted to describe the event only if they answered “yes”. If they answered “no”, they were not asked, therefore, NA values in these columns are expected.

Note that exploring NA values is different from exploring participant compliance (their proportion of answered beeps). We will explore participant compliance in a later step.

First, we can use the vis_miss() function from the visdat package to visualize the missing values in the dataset. The x axis represents the variables in the dataset and the y axis represents the rows. The black cells represent present values, and the white cells represent missing values.

We can see that there are some columns with a lot of missing values, which is expected since we are displaying data from two different questionnaires. We can also see that there are some participants that did not sign up with a “code”.

library(ggplot2)
library(visdat)

# This plot makes more sense if the data is ordered by `participant` and `start`
data <- arrange(data, participant, start)

vis_miss(data) + 
  ggtitle("Missing values")

We have also seen that some columns may have all missing values. It would be interesting to check which columns they are, and remove them if they are not useful for the analysis.

# Create a helper function that checks whether all missing values are missing
all_na <- function(col) all(is.na(col))

# Get cols that have all NA
na_cols <- lapply(data, all_na) 
na_cols <- as.logical(na_cols)

# checking the names of these columns. We have 4 cols with all NA
names(data)[na_cols] 
#> [1] "location_likert"         "company_likert"          "activity_likert"        
#> [4] "evening_activity_likert"

The four columns with all NA are: location_likert, company_likert, activity_likert, evening_activity_likert. These correspond to multiple choice questions where an “Answer value” was not provided during questionnaire creation. Therefore, it is completely expected that these columns are empty. We will remove them with:

data <- data[, !na_cols] # removing cols with all NA

# Tidyverse alternative
data <- select(data, -where(all_na))

We can also see that the missed beeps have NAs in the start variable, and the answered beeps do not. We can use this information to create an additional variable that indicates whether the beep was answered or not.

data <- data |> 
  mutate(answered = !is.na(start)) # missed beeps will be FALSE, answered beeps will be TRUE

Reformat time stamps

The time stamps from m-Path are in Unix time but with respect to the local time zone of the participant. To work with them, we will reformat them to a more readable format. We will use the timestamps_to_datetime function from mpathr, that will put the dates in this format: "%Y-%m-%d %H:%M:%OS". We are specifying the time zone as "Europe/Brussels" because we know our participants were in this time zone. However, if you are not merging the data with any other dataset, the timezone is not crucial and can be left out.

data <- data |> 
  mutate(across(
    .cols = c("scheduled", "sent", "start", "stop"),
    .fns = \(x) timestamps_to_datetime(x, force_tz = "Europe/Brussels")
  ))

Now that the dates are reformatted, we can create some other important time variables:

  1. We create a column obs_n that for each participant, shows the number of observations in the order they were sent (starting from 1 to 110, which is the total number of observations per participant).

  2. The column day_n shows the number of days since the first beep was sent for each participant.

  3. The column obs_n_day shows the observation number within a day for each participant (the first beep of the day will be 1, until the last beep of the day: 11).

library(lubridate)

# Let"s first create a helper function to calculate the day number (1, 2, etc.) since the
# start of the study. By using `group_by()` before calling this function, the function
# is called each time with a vector of dates corresponding to the same participant.
day_number <- function(vec) {
  # Transform the vector to dates Note that lubridate::date()` takes into account the time
  # zone, while `as.Date()` does not.
  vec <- date(vec)
  
  # Get the first day in the vector
  day_one <- min(vec, na.rm = TRUE)
  
  # Calculate the difference in days, and add 1 to start from 1
  diffs <- difftime(vec, day_one, units = "days") + 1
  
  # Convert the difftime objects to integers
  as.integer(diffs)
}

# First sort the data by participant and sent time because ordering matters
data <- arrange(data, participant, sent)

# For each participant, calculate the total observation number. Since each beep is a row,
# we can do this using `row_number()`.
# Also for each participant, calculate the day number since the start of the study.
data <- data |> 
  group_by(participant) |> 
  mutate(obs_n = row_number()) |> 
  mutate(day_n = day_number(sent)) |>
  ungroup()

# Then calculate the observation number within a day for each participant, again using
# `row_number()`.  
# Create observation number within day
data <- data |>   
  group_by(participant, day_n) |> 
  mutate(obs_n_day = row_number()) |> 
  ungroup()

Merging with external data sources

Here, we will show how to merge the current dataset with an external dataset.

In the external dataset, we have data that corresponds to the average heart rate throughout the day (note: unlike the other data, the external data does not correspond to real data, it was simulated).

# Load the external dataset
external_data <- read.csv("external_dataset_bpm.csv")

# Check the dataset
head(external_data) 
#> # A tibble: 6 × 3
#>   code       day_n bpm_day
#>   <chr>      <int>   <dbl>
#> 1 !byyo kjyt     1    60.2
#> 2 !bxxm dqfu     1    81.8
#> 3 !bxxm dqfu     2    74.4
#> 4 !bxxm dqfu     3    90.2
#> 5 !bxxm dqfu     4    93.1
#> 6 !bxxm dqfu     5    74.8

The external dataset has the columns code, day_n and bpm_day, for each participant and for each day we have the mean heart rate.

Since the value to link the two datasets is in code, we should check that the codes are in order (e.g. are there missing codes?). We can check that each code corresponds to one participant by examining the variable”s coherence. We can see here that one participant does not have a code, so we will not be able to get their heart rate data. But otherwise it seems like each code corresponds to one participant:

distinct(data, participant, code) 
#> # A tibble: 20 × 2
#>    participant code      
#>          <int> <chr>     
#>  1           1 !byyo kjyt
#>  2           2 !bxxm dqfu
#>  3           3 !xoub vhcz
#>  4           4 !yyzt qvtn
#>  5           5 !zdpo cqrd
#>  6           6 !hrhm eahp
#>  7           7 !ajxz ckfk
#>  8           8 <NA>      
#>  9           9 !kfvt zyxq
#> 10          10 !emet ekgu
#> 11          11 !csns cnzc
#> 12          12 !mhrn creh
#> 13          13 !xxgo maxo
#> 14          14 !oekk qcxq
#> 15          15 !btha duvg
#> 16          16 !tykg oqoq
#> 17          17 !cvfh ekvf
#> 18          18 !ngyw egam
#> 19          19 !xjsj dzfd
#> 20          20 !vkqo ante

We can use the following code to merge it with the main dataset:

# We merge by code and by day because we have a heart rate mean for each participant and
# each day
data <- left_join(
  x = data,
  y = external_data, 
  by = c("code", "day_n")
) 

Handling nested data

When participants can provide multiple choices to a question, read_mpath() stores these as vectors such as list columns of nested data, where each values is a vectorc(1,2,4):

Participant day choice
1 1 c(1, 2, 4)
1 2 c(2, 3)
1 3 c(4)

To deal with these responses and analyze them, we can convert the data to a long format: (where each choice is a separate row), like this:

Participant day choice
1 1 1
1 1 2
1 1 4

We can also convert the data to a wide format (where each choice is a separate column).

Participant day choice_1 choice_2 choice_3
1 1 1 2 4

The following data exemplifies how to convert the these kind of columns both to a long and to a wide format. Using our column evening_activity_index, which contains vectors of which activities people had done during a specific day:

# Delete rows with an NA in this column
activity_data <- data |> 
  select("participant", "day_n", "evening_activity_index") |> 
  filter(!is.na(evening_activity_index))

activity_data
#> # A tibble: 141 × 3
#>    participant day_n evening_activity_index
#>          <int> <int> <list>                
#>  1           1     1 <int [3]>             
#>  2           2     1 <int [1]>             
#>  3           2     2 <int [5]>             
#>  4           2     3 <int [4]>             
#>  5           2     4 <int [4]>             
#>  6           2     5 <int [4]>             
#>  7           2     8 <int [4]>             
#>  8           2     9 <int [3]>             
#>  9           3     6 <int [2]>             
#> 10           3     7 <int [5]>             
#> # ℹ 131 more rows

Note that in the above code, we delete rows with NA in the column evening_activity_index. In our case, participants were not allowed to leave this question blank, so NAs indicate unanswered beeps. In other cases, NAs may indicate that the person reported doing none of the activities listed, so it maybe would not be a good idea to delete these rows, as they are still informative.

Long format

# Convert to long format using unnest()
long_data <- activity_data |>  
  unnest(evening_activity_index)

long_data
#> # A tibble: 475 × 3
#>    participant day_n evening_activity_index
#>          <int> <int>                  <int>
#>  1           1     1                      1
#>  2           1     1                      3
#>  3           1     1                      4
#>  4           2     1                      1
#>  5           2     2                      1
#>  6           2     2                      2
#>  7           2     2                      4
#>  8           2     2                      5
#>  9           2     2                      6
#> 10           2     3                      1
#> # ℹ 465 more rows

Wide format

wide_data <- activity_data |> 
  unnest_wider(evening_activity_index, names_sep = "_")

wide_data
#> # A tibble: 141 × 8
#>    participant day_n evening_activity_index_1 evening_activity_index_2 evening_activity_index_3
#>          <int> <int>                    <int>                    <int>                    <int>
#>  1           1     1                        1                        3                        4
#>  2           2     1                        1                       NA                       NA
#>  3           2     2                        1                        2                        4
#>  4           2     3                        1                        3                        4
#>  5           2     4                        2                        3                        4
#>  6           2     5                        1                        3                        4
#>  7           2     8                        1                        2                        4
#>  8           2     9                        1                        4                        6
#>  9           3     6                        1                        6                       NA
#> 10           3     7                        1                        2                        5
#> # ℹ 131 more rows
#> # ℹ 3 more variables: evening_activity_index_4 <int>, evening_activity_index_5 <int>,
#> #   evening_activity_index_6 <int>

Another option for a wide data frame is to have binary columns corresponding to an choice. Like this:

Participant day choice_1 choice_2 choice_3
1 1 0 1 0
2 1 1 1 1

In this table, participant one chose choice 2, but didn”t choose choice 1 or 3, while participant 2 chose all three options. This can be done with the following code:

data_wide_binary <- activity_data |> 
  unnest(evening_activity_index) 

data_wide_binary <- data_wide_binary |> 
  mutate(activity = paste0("activity_", evening_activity_index)) |> 
  pivot_wider(
    names_from = "activity",
    values_from = "evening_activity_index",
    values_fill = list(evening_activity_index = 0),
    values_fn = list(evening_activity_index = length)
  )

data_wide_binary
#> # A tibble: 141 × 8
#>    participant day_n activity_1 activity_3 activity_4 activity_2 activity_5 activity_6
#>          <int> <int>      <int>      <int>      <int>      <int>      <int>      <int>
#>  1           1     1          1          1          1          0          0          0
#>  2           2     1          1          0          0          0          0          0
#>  3           2     2          1          0          1          1          1          1
#>  4           2     3          1          1          1          0          0          1
#>  5           2     4          0          1          1          1          0          1
#>  6           2     5          1          1          1          0          0          1
#>  7           2     8          1          1          1          1          0          0
#>  8           2     9          1          0          1          0          0          1
#>  9           3     6          1          0          0          0          0          1
#> 10           3     7          1          0          1          1          1          1
#> # ℹ 131 more rows

Re-ordering columns

As a final step, we will re-order the columns using the relocate function from dplyr:

data <- data |> 
  relocate("obs_n", "day_n", "obs_n_day", "answered", "bpm_day", .before = "gender")

Step 2 : Design and sampling scheme

In this step, we explore the design and sampling scheme of the study. The main goal is to examine to what extent the data collection process went as expected, and to identify potential issues that could motivate data exclusion. We mainly will check the coherence of the time stamps, the sent beeps and the duration of the study.

Coherence time stamps and observations order

In the following sections, we perform some logical tests to ensure the coherence of the time stamps and the observations. For example: we expect that for one observation, the scheduled time should always be before the sent time. Having beeps where this is not the case could indicate an error in the data collection process. For more information on time stamp coherence and how to check it, see the ESM Preprocessing Gallery.

Coherence within observations

Within a single observation, we expect that the scheduled time is before the sent time, the sent time is before the start time, and the start time is before the stop time. We will check this for all observations.

# Checking that scheduled < sent < start < stop

# check if any scheduled time is after the sent time
any(data$scheduled > data$sent & !is.na(data$sent)) 
#> [1] FALSE
# check if any sent time is after the start time
any(data$sent > data$start & !is.na(data$start)) 
#> [1] FALSE
# check if any start time is after the stop time
any(data$start > data$stop & !is.na(data$stop)) 
#> [1] FALSE

It all returns FALSE, which means that all observations are in the expected order.

If some observations were not in the expected order, this could be due to participants changing time zones, or changing from summer to winter times (or the other way around).

Coherence between observations

We should also verify that the time stamps are in the correct order between observations. We expect that the scheduled time of the current observation is before the scheduled time of the next observation, and the same for the sent, start, and stop times.

However, it may be that participants answered the 6th beep before the 5th beep, due to close beep times and overlapping expiration times. In this case, maybe the order of the observations could be reversed, so that observation 5 becomes observation 6.

To check for coherence between observations, we order the data frame according to one of our time variables, and check for issues between consecutive observations.

# check if the scheduled time of the current observation is after the scheduled time of
# the previous observation
# show only rows where one of these issues is present
data |> 
  select(participant, "scheduled", "sent", "start", "stop") |> 
  arrange(participant, sent) |> 
  group_by(participant) |> 
  mutate(
    sched_lag_issue = lag(scheduled) > scheduled, 
    sent_lag_issue = lag(sent) > sent,
    start_lag_issue = lag(start) > start,
    stop_lag_issue = lag(stop) > stop
  ) |> 
  filter(sched_lag_issue | sent_lag_issue | start_lag_issue | stop_lag_issue) 
#> # A tibble: 1 × 9
#>   participant scheduled           sent                start               stop               
#>         <int> <dttm>              <dttm>              <dttm>              <dttm>             
#> 1          16 2024-05-13 22:10:00 2024-05-13 22:10:01 2024-05-13 22:10:05 2024-05-13 22:10:49
#> # ℹ 4 more variables: sched_lag_issue <lgl>, sent_lag_issue <lgl>, start_lag_issue <lgl>,
#> #   stop_lag_issue <lgl>

We have a row that shows a start_lag issue, upon further inspection, we see that this is because the participant filled a main questionnaire beep just after the evening questionnaire.

This is okay, because in the evening questionnaire participants answer about their whole day, and in the “main questionnaire” beep they answer about how they feel in that moment.

Duration of the study per participant

We will now check the duration of the study by plotting the earliest and latest sent date. We expect that the duration of the study is the 10 days for everyone.

# Load the `ggalt` package for `geom_dumbbell`
library(ggalt)

# Calculate the beginning and end time of the study for each participant
study_duration <- data |> 
  group_by(participant) |> 
  summarise(
    from = min(sent, na.rm = TRUE), 
    to = max(sent, na.rm = TRUE)
  ) |> 
  mutate(participant = as.factor(participant)) # For ggplot

# Create a plot showing the study run time for each participant
study_duration |> 
  ggplot(aes(
    x = from, 
    xend = to, 
    y = participant
  )) + 
  geom_dumbbell(color = dark_mpath_palette[1], size = 0.75) +
  scale_x_datetime(date_breaks = "1 month", date_labels = "%B %y") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  labs(
    title = "Duration of the study per participant",
    x = "Time",
    y = "Participant"
  )

# Verify that there are 10 days for all participants
data |>
  group_by(participant) |> 
  summarise(max_days = max(day_n))
#> # A tibble: 20 × 2
#>    participant max_days
#>          <int>    <int>
#>  1           1       10
#>  2           2       10
#>  3           3       10
#>  4           4       10
#>  5           5       10
#>  6           6       10
#>  7           7       10
#>  8           8       10
#>  9           9       10
#> 10          10       10
#> 11          11       10
#> 12          12       10
#> 13          13       10
#> 14          14       10
#> 15          15       10
#> 16          16       10
#> 17          17       10
#> 18          18       10
#> 19          19       10
#> 20          20       10

Number of beeps sent per participant

All participants have the same number of beeps sent, as expected:

n_sent <- data |> 
  drop_na(sent) |> 
  group_by(participant) |> 
  summarize(n = n()) 

# Create a bar chart with the number of beeps sent per participant
n_sent |> 
  ggplot(aes(
    x = factor(participant), # factor() is needed to make them into categories
    y = n
  )) +
  geom_col(position = "dodge", fill = dark_mpath_palette[2]) +
  labs(
    title = "Number of beeps sent per participant",
    x = "Participant",
    y = "Number of beeps"
  )

Delay between two consecutive beeps

Most delays are around 1 hour because that”s how we spaced the beeps during the day. The delays at around 10 hours correspond to the delay between the last beep of the day (evening questionnaire) and the first beep of the next day.

df_int <- data |>
  arrange(participant, sent) |>
  group_by(participant) |>
  mutate(delay = difftime(lead(sent), sent, units = "hours")) 

df_int |> 
  ggplot(aes(x = delay)) +
  geom_histogram(bins = 100, fill = dark_mpath_palette[3]) + 
  labs(
    title = "Delay between two consecutive beeps",
    x = "Delay between two consecutive beeps (hours)",
    y = "Count"
  )

Conclusions: Design and sampling scheme

In general, we did not find any issues with the experimental design:

  • The time stamps are coherent.
  • The number of beeps sent is equal for all participants, which in our case was intended (note that in some experiments, participants may receive different beeps depending on their experimental group, or other variables. In which case an equal number of beeps per participant would be a problem).
  • The delays between beeps are as expected.
  • The duration of the study per participant is also consistent (we expected an equal duration of 10 days per participant).

We can now move on to the next steps of the pre-processing.

Note: we followed steps that were relevant for our study design, for a more comprehensive overview of possible checks for the sampling and study design, see the ESM Preprocessing Gallery.

Step 3: Participants response behaviors

In this step, we want to look more closely at participants” responses. We will look at whether participants seemed to follow the sampling scheme correctly, and whether there are any response patterns that could reduce data quality. We primarily will look at the sampling scheme per participant, the delay to fill beeps, the time of answered and missed beeps, and participant compliance.

For a more comprehensive overview of possible checks for this step, see the ESM Preprocessing Gallery.

Sampling scheme per participant

Beep level

With the plot below, we can explore whether there are patterns in participant”s response rates throughout the study.

In the following plot you can see the number of beeps answered per participant, per observation. The vertical lines separate the days.

Participant 1 and 16 seemed to answer more beeps at the start of the study than later on, and there are some considerable gaps for some participants (e.g. participant 2). But otherwise there”s no clear pattern.

data |> 
  filter(answered) |>  # filtering by answered beeps, another option is drop_na(start)
  ggplot(aes(x = obs_n, y = factor(participant))) +
  geom_point(
    size = 1.5, 
    col = dark_mpath_palette[1]
  ) +
  geom_vline(
    xintercept = seq.int(from = 11.5, to = max(data$obs_n), by = 11), 
    color = mpath_palette[3], 
    alpha = 0.5
  ) +
  labs(
    title = "Answered beeps per participant",
    x = "Observation number",
    y = "Participant"
  )

Delay to start and to fill

We will now look at the delay between when participants received a beep and when they actually answered it (the delay in starting the beep), and how long it takes participants to fill the beep.

We will plot the delay to start and to fill separating per questionnaire, this is because the two different questionnaires had a different expiration time. We can see that most delays to answer are quite small. Some answers for the evening questionnaire took a long time, this was because the evening questionnaire had an expiration of 3 hours, while participants were only able to answer beeps for the main questionnaire in the first 30 minutes.

data <- data |> 
  mutate(
    delay_start_min = difftime(start, sent, units = "mins"),
    delay_end_min = difftime(stop, start, units = "mins") 
  ) 

data |> 
  filter(answered)  |> 
  ggplot(aes(x = delay_start_min, fill = questionnaire)) +
  geom_histogram(bins = 100, alpha = 0.7, position = "identity") + 
  scale_fill_manual(values = dark_mpath_palette) +
  labs(
    title = "Delay to start per questionnaire",
    x = "Delay to start (minutes)",
    y = "Count"
  )
#> Don't know how to automatically pick scale for object of type <difftime>. Defaulting to continuous.

Time of answered/missed beeps

Here, we will explore the time of answered and missed beeps. If these histograms are very different, it may indicate that participants are missing beeps at certain times of the day (for example, it could be that participants miss more beeps in the morning than in the afternoon).

The plots look very similar. We can see that the last beeps were sent at around 22h, but there are answered beeps as late as 1h in the night. This is because the expiration date for the evening questionnaire was set at 3 hours, so some participants may have answered these beeps quite late. The expiration for the rest of the beeps was around 30 minutes.

# To use the function `date_breaks()`
library(scales)

# Hour of day from answered beeps
# Extract the hour, minute and seconds from the `start` time with `format()`.
data |> 
  filter(answered) |> 
  mutate(start = format(start, format = "%H:%M:%S")) |> 
  ggplot(aes(x = hms(start))) +
  geom_histogram(bins = 100, fill = dark_mpath_palette[1]) +
  scale_x_time(
    limits = hms(c("00:00:00", "24:00:00")), 
    breaks = date_breaks("2 hours"),
    labels = function(x) strftime(x, format = "%H:%M")
  ) +
  labs(
    title = "Time of answered beeps",
    x = "Time",
    y = "Count"
  )

# Hour of day of missed beeps
# Extract the hour, minute and seconds from the `sent` time with `format()`.
data |> 
  filter(!answered) |> 
  mutate(sent = format(sent, format = "%H:%M:%S")) |> 
  ggplot(aes(x = hms(sent))) +
  geom_histogram(bins = 100, fill = dark_mpath_palette[2]) +
  scale_x_time(
    limits = hms(c("00:00:00", "24:00:00")), 
    breaks = date_breaks("2 hours"),
    labels = function(x) strftime(x, format = "%H:%M")
  )+
  labs(
    title = "Time of missed beeps",
    x = "Time",
    y = "Count"
  )

Compliance rate

Compliance is the proportion of answered beeps. It is important to have a look at this metric, to identify participants that have missed a lot of beeps. We can use the function response_rate from the mpathr package to calculate the response rate.

# Get the response rates
response_rates <- response_rate(
  data = data,
  valid_col = answered, 
  participant_col = participant
)

response_rates |>
  group_by(participant) |>
  ggplot(aes(x = participant, y = response_rate)) +
  geom_bar(stat = "identity", fill = dark_mpath_palette[3]) +
  labs(
    title = "Compliance per participant",
    x = "Participant",
    y = "Compliance (proportion of answered beeps)"
  )

Additionally, we can just print a summary of each participant and their compliance:

print(response_rates)
#> # A tibble: 20 × 3
#>    participant number_of_beeps response_rate
#>          <int>           <int>         <dbl>
#>  1           1             110        0.118 
#>  2           2             110        0.418 
#>  3           3             110        0.564 
#>  4           4             110        0.845 
#>  5           5             110        0.9   
#>  6           6             110        0.664 
#>  7           7             110        0.673 
#>  8           8             110        0.0818
#>  9           9             110        0.545 
#> 10          10             110        0.873 
#> 11          11             110        0.836 
#> 12          12             110        0.9   
#> 13          13             110        0.8   
#> 14          14             110        0.755 
#> 15          15             110        0.682 
#> 16          16             110        0.318 
#> 17          17             110        0.791 
#> 18          18             110        0.818 
#> 19          19             110        0.636 
#> 20          20             110        0.436

We can see that some participants have a very low compliance rate (e.g. participant 1 with a compliance of 11%). We may want to exclude these participants from the analysis.

# Remove participants with a response rate lower than 30%
low_response <- response_rates |>
  filter(response_rate < 0.3) |>
  pull("participant") # Pull out a variable as a vector

data <- data |>
  filter(!(participant %in% low_response))

Detecting dishonesty

In cases where participants receive incentives based on how many questionnaires they filled, they could manually change the time on their phone to fill in older questionnaires. In order to detect this, we have the column phone_server_offset, which is the difference between the phone time and the server time.

If participants change the time on their phone, this column will show a large difference. We can check if there are any participants with a large difference in the phone_server_offset column.

data |>
  mutate(phone_server_offset = abs(phone_server_offset / 60)) |>
  ggplot(aes(x = abs(phone_server_offset))) +
  geom_histogram(
    bins = 100, 
    alpha = 0.7, 
    position = "identity", 
    fill = mpath_palette[1]
  ) +
  labs(
    title = "Difference between phone and server time",
    x = "Phone-server offset (minutes)",
    y = "Count"
  )

We can see that the maximum difference is around 3 minutes. Since this is not a large difference, it seems like participants didn”t change the time on their phone during the study.

Conclusions: Participants response behaviors

In this section, we examined participants” response behaviors and identified some patterns. For example, we found and removed participants with a very low compliance rate.

For more guidelines on how to conduct similar checks, see ESM Preprocessing Gallery.

Step 4: Compute and transform variables

In this step we will compute important variables to prepare the data for our analysis.

We will conduct the following two analyses:

  1. Analysis 1: Do participants have more negative affect when they are alone?
  2. Analysis 2: Is the evening-reported anxiety level predicted well with a combination of the mean anxiety levels and the maximum anxiety level experienced throughout the day?

Data preparation for analysis 1

Computing affect scores

For the first analysis, we need to compute a measure of negative affect. This will be the mean of the sad, angry, anxious, and tired sliders.

data <- data |> 
  mutate(negative_affect = rowMeans(pick(
    "slider_sad", "slider_angry", "slider_anxious", "slider_tired"
  )))

Computing binary variables from multiple choice questions

For the first analysis, we also need to create a binary variable that indicates whether the participant was alone or not. We can use the company_string variable to create it.

The company_string column contains the response options that indicate the participant’s social status:

  • Option 1: “Alone”
  • Option 2: “With one person I know”
  • Option 3: “With multiple people I know”
  • Option 4: “With one or more people I don”t know”

We will create a new variable called alone that is TRUE if the participant was alone, and FALSE if they were not.

data <- data |>
  mutate(alone = company_string == "Alone") 

This is the only transformation we need for the first analysis. We can save away the data from this analysis to data_analysis_1, where we also filter out unanswered beeps (or beeps that do not correspond to the main questionnaire) and only keep the variables we need for the analysis.

data_analysis_1 <- data |> 
  filter(questionnaire == "main_questionnaire" & answered) |> 
  select("participant", "negative_affect", "alone")

Data preparation for analysis 2

Filtering out participants and days with small response rate

For the second analysis, we will eliminate participant”s data if they had less than 50% compliance rate:

# Calculate which participants have a compliance rate of less than 50%
participants_to_rm <- response_rate(
  data = data, 
  valid_col = answered, 
  participant_col = participant
) |>
  filter(response_rate < 0.5) |> 
  pull("participant")
#> Calculating response rates for the entire duration of the study.
# these are the participants we will remove for the 2nd analysis
print(participants_to_rm) 
#> [1]  2 16 20

Now that we know which participants to remove, we just filter them out and save the resulting data frame to data_analysis_2.

# filters out participants that are present in participants_to_rm
data_analysis_2 <- data |>
  filter(!(participant %in% participants_to_rm)) 

Also, for every participant, if they answered less than 50% of the beeps on a given day, we will remove that day”s data from the analysis. This is important since we will take the mean, and we would not want to analyze means of very few values.

days_below_50 <- data_analysis_2 |>
  group_by(participant, day_n) |> 
  summarize(na_prop = sum(answered) / max(obs_n_day)) |> 
  filter(na_prop < 0.5)

# Use an anti join to remove elements from `data_analysis_2` that match those in 
# `days_below_50`
data_analysis_2 <- data_analysis_2 |>
  anti_join(days_below_50, by = c("participant", "day_n")) 

Computing the maximum and mean anxiety level

We will calculate the maximum and the mean anxiety level (in slider_anxious) experienced by each participant in each day. We can do this with the following code:

data_analysis_2 <- data_analysis_2 |>
  group_by(participant, day_n) |>
  mutate(
    max_anxious = max(slider_anxious, na.rm = TRUE),
    mean_anxious = mean(slider_anxious, na.rm = TRUE)
  ) |> 
  ungroup() |>
  filter(questionnaire == "evening_questionnaire" & answered) |>
  select("participant", "day_n", "evening_slider_anxious", "max_anxious", "mean_anxious") 

Step 5: Data exploration

Now, we will explore the data a bit more to get to know the distribution and characteristics of some important variables.

Correlation between negative affect variables

Since we are pooling together all the negative affect variables into a composite variable (negative affect), it”s interesting to see how related they are. We can quickly visualize the correlations between these variables in the plot below:

library(GGally)

data |> 
  select("slider_anxious", "slider_sad", "slider_angry","slider_tired") |> 
  drop_na() |> # Drop all missing values
  ggpairs()

As expected, correlations are positive, but most correlations between the variables are rather weak.

Along the diagonal, we can see the distribution of the variables. Most distributions are clearly skewed to the right. Which means that most of the time, participants report low levels of anxiety, sadness and anger. However, participants report higher levels of tiredness.

Alone vs. not alone

We could look at how often participants reported being alone. We can see that the proportion of time alone greatly varies between individuals. Some participants report being alone almost 90% of the time, while some others are accompanied mostly 90% of the time.

alone_data <- data |>
  drop_na(alone) |> 
  mutate(company_string = factor(company_string, levels = c(
    "Alone", 
    "With one person I know", 
    "With multiple people I know", 
    "With one or more people I don't know"
  ))) |> 
  count(participant, company_string)
  
alone_data |> 
  ggplot(aes(
    x = participant,
    y = n,
    fill = company_string
  )) +
  geom_bar(position = "fill", stat = "identity") +
  scale_fill_manual(
    name = "Type of company",
    values = c(mpath_palette[c(1,3)], "#DB8908", mpath_palette[2])
  ) +
  labs(
    title = "Proportion of time alone or accompanied",
    x = "Participant",
    y = "Count"
  )

For the analysis, we will pool together reports of being with multiple people, or being with only one person. We will also consider being with one or more people that the participant does not know as being accompanied.

Emotion variables

We can also have a look at the distribution of the emotion sliders. We can mainly see that the negative emotions (angry, anxious, sad and tired) tend to be skewed to the right, which means that most of the time participants report low levels of these emotions. “Tired” seems to be an exception to this trend. Meanwhile, the positive emotions (happy, relaxed and energetic) tend to be skewed to the left, which means that most of the time participants report high levels of these emotions.

data |> 
  pivot_longer("slider_happy":"slider_tired") |> 
  ggplot(aes(x = value)) +
  geom_histogram(aes(y = after_stat(density)), alpha = 0.7) + 
  geom_density(alpha = 0.5) +
  facet_wrap(vars(name), scales = "free_y")

Step 6: Analysis

Question 1: Do participants have more negative affect when they are alone?

We will assess this question with a mixed model, to account for the random effect of participants. We will use the following formula:

\[\text{negative affect ~ alone + (1 + alone | participant)}\] With this formula, we specify an intercept for every participant, as well as random slopes for alone.

library(lme4)

model_1 <- lmer(
  formula = negative_affect ~ alone + (1 + alone | participant), 
  data = data_analysis_1
)

Before looking at the results, we should check that our mixed model”s assumptions are met. In our case, it does not make sense to look at linearity because our only predictor (alone) is a binary variable. However, we can check for homoscedasticity and normality of residuals:

Homoscedasticity: We can check for homoscedasticity (also known as the homogeneity of variance) by using a function of the performance package which is part of the larger framework of easystats. While the Breusch-Pagan test that is run by check_heteroscedasticity() was significant (\(p<.001\)), a visual inspect tells us that there is only a very minor overall trend in the variance of the residuals, which is unlikely to influence the results.

library(performance)

check_heteroscedasticity(model_1)
#> Warning: Heteroscedasticity (non-constant error variance) detected (p < .001).
plot(check_heteroscedasticity(model_1))

Normality: We can also check for normality of the residuals by using the check_normality() function. The Shapiro-Wilk test that is run by this function yielded a significant result (\(p<.001\)). A visual inspection shows that the residuals are not normally distributed for the lower quantiles and are slightly higher in the upper quantiles.

check_normality(model_1)
#> Warning: Non-normality of residuals detected (p < .001).
plot(check_normality(model_1))

We can now check the results of the model:

summary(model_1) 
#> Linear mixed model fit by REML ['lmerMod']
#> Formula: negative_affect ~ alone + (1 + alone | participant)
#>    Data: data_analysis_1
#> 
#> REML criterion at convergence: 9719.9
#> 
#> Scaled residuals: 
#>     Min      1Q  Median      3Q     Max 
#> -2.5088 -0.5837 -0.0643  0.5183  4.4882 
#> 
#> Random effects:
#>  Groups      Name        Variance Std.Dev. Corr 
#>  participant (Intercept)  86.93    9.324        
#>              aloneTRUE    24.73    4.973   -0.15
#>  Residual                147.70   12.153        
#> Number of obs: 1231, groups:  participant, 18
#> 
#> Fixed effects:
#>             Estimate Std. Error t value
#> (Intercept)   24.117      2.266   10.64
#> aloneTRUE      2.554      1.459    1.75
#> 
#> Correlation of Fixed Effects:
#>           (Intr)
#> aloneTRUE -0.213
# Chi-square test to see if the effects are different from 0
library(car)
Anova(model_1)
#> # A tibble: 1 × 3
#>   Chisq    Df `Pr(>Chisq)`
#>   <dbl> <dbl>        <dbl>
#> 1  3.06     1       0.0802

From the results, we can see that the effect of being alone on negative affect is not significant (p = 0.08). It does not seem like participants have more negative affect when they are alone. However, we are using a very small dataset, which results in very low power.

We can visualize the effect by plotting the values of the negative affect for the groups (alone and not alone) as well as the mean values for each group.

data_analysis_1$alone <- as.factor(data_analysis_1$alone)
levels(data_analysis_1$alone) <- c("Not Alone", "Alone")

data_analysis_1 |> 
  ggplot(aes(x = alone, y = negative_affect)) +
  geom_jitter(
    width = 0.07, 
    height = 0, 
    alpha = 0.2, 
    size = 2, 
    color = mpath_palette[1]
  ) +
  stat_summary(
    geom = "point", 
    fun = mean, 
    color = mpath_palette[2], 
    size = 2.5
  ) + 
  stat_summary(
    mapping = aes(group = 1),
    geom = "line",
    fun = mean, 
    color = mpath_palette[2], 
    linewidth = 1
  ) +
  stat_summary(
    geom = "errorbar",
    fun.data = mean_se, 
    width = 0.04, 
    color = mpath_palette[2]
  ) +
  labs(
    title = "Negative Affect by Alone",
    x = "Alone",
    y = "Negative Affect"
  ) +
  theme_minimal()

Alternatively, we can visualize this using the modelbased package which is part of easystats.

library(modelbased)

estimate_means(model_1, by = "alone") |> 
  plot(
    point = list(color = mpath_palette[1]), # Customise some of the aesthetics
    line = list(color = mpath_palette[2]),
    pointrange = list(color = mpath_palette[2])
  ) +
  theme_minimal() +
  labs(
    title = "Negative Affect by Alone",
    x = "Alone",
    y = "Negative Affect"
  )

Question 2: Is the evening-reported anxiety level predicted well with the mean and the maximum anxiety level?

In this second analysis, we want to see whether the overall anxiety levels throughout the day (as reported in the evening questionnaire) can be well predicted by the average anxiety levels and the maximum anxiety levels experienced throughout the day. We will use the following model formula:

\[\text{anxiety (evening) ~ mean(anxiety) + max(anxiety) + (1 | participant)}\]

model_2 <- lmer(
  formula = evening_slider_anxious ~ mean_anxious + max_anxious + (1 | participant), 
  data = data_analysis_2
)

Before looking at the results, we should check that our mixed model”s assumptions are met:

Linearity: the points fall roughly along a straight line, so the linearity assumption is met.

check_collinearity(model_2)
#> # A tibble: 2 × 8
#>   Term           VIF VIF_CI_low VIF_CI_high SE_factor Tolerance Tolerance_CI_low Tolerance_CI_high
#>   <chr>        <dbl>      <dbl>       <dbl>     <dbl>     <dbl>            <dbl>             <dbl>
#> 1 mean_anxious  3.02       2.31        4.11      1.74     0.331            0.243             0.433
#> 2 max_anxious   3.02       2.31        4.11      1.74     0.331            0.243             0.433
plot(check_collinearity(model_2))

Homoscedasticity: there is no clear pattern in the residuals vs. fitted values plot, so the homoscedasticity assumption is also met.

check_heteroscedasticity(model_2)
#> Warning: Heteroscedasticity (non-constant error variance) detected (p < .001).
plot(check_heteroscedasticity(model_2))

Normality: it seems like there is more data located at the extremes of the distribution and less data at the center. However, the residuals are roughly normally distributed.

check_normality(model_2) 
#> OK: residuals appear as normally distributed (p = 0.166).
plot(check_normality(model_2))

Now that we have checked the assumptions. We can move on to the results:

summary(model_2)
#> Linear mixed model fit by REML ['lmerMod']
#> Formula: evening_slider_anxious ~ mean_anxious + max_anxious + (1 | participant)
#>    Data: data_analysis_2
#> 
#> REML criterion at convergence: 913.9
#> 
#> Scaled residuals: 
#>      Min       1Q   Median       3Q      Max 
#> -2.87289 -0.60681  0.02203  0.55272  2.80247 
#> 
#> Random effects:
#>  Groups      Name        Variance Std.Dev.
#>  participant (Intercept)  24.3     4.93   
#>  Residual                138.2    11.76   
#> Number of obs: 116, groups:  participant, 15
#> 
#> Fixed effects:
#>              Estimate Std. Error t value
#> (Intercept)  -3.45134    3.10244  -1.112
#> mean_anxious  0.66171    0.12947   5.111
#> max_anxious   0.34462    0.09186   3.752
#> 
#> Correlation of Fixed Effects:
#>             (Intr) mn_nxs
#> mean_anxios  0.024       
#> max_anxious -0.500 -0.818
Anova(model_2)
#> # A tibble: 2 × 3
#>   Chisq    Df `Pr(>Chisq)`
#>   <dbl> <dbl>        <dbl>
#> 1  26.1     1  0.000000320
#> 2  14.1     1  0.000176

Both the mean and maximum anxiety level are significant predictors of the evening-reported anxiety. Going further, we may wonder how well our model predicts the evening-reported anxiety levels. For this, we can compute the \(R^2\). We can use the rsq package, which implements a method for calculating it in mixed models.

The \(R^2\) of mixed models usually comes unpacked into three different values:

  • The proportion of variance explained by both the fixed and random effects, in $model (it is the sum of the two \(R^2\) below).
  • The proportion of variance explained by the fixed effects, in $fixed.
  • The proportion of variance explained by the random effects, in $random.

Since our random effects are just to control for the effect of different participants. We mainly want to look at the \(R^2\) in $fixed.

library(rsq)

rsq.lmm(model_2)
#> $model
#> [1] 0.8087958
#> 
#> $fixed
#> [1] 0.7767994
#> 
#> $random
#> [1] 0.03199634

Analysis: Conclusion

In this small analysis we assessed whether being alone influenced negative affect, and whether the mean and maximum anxiety experienced throughout the day could predict the evening-reported anxiety level.

We mainly found that being alone does not seem to lead to higher negative affect levels. Maybe the way we categorized being alone was not sensitive enough to capture the effect. For example, we categorized the response option of being with “multiple people I don”t know” as not being alone, but it would also make sense to categorize it as being alone.

In the second analysis, we found that the mean and maximum anxiety levels experienced throughout the day are significant predictors of the evening-reported anxiety level. This shows that both the general level of anxiety, as well as the magnitude of anxiety peaks affect the appraisal of overall anxiety throughout the day.

LS0tCnRpdGxlOiAiSW1wb3J0aW5nIGFuZCBwcmVwcm9jZXNzaW5nIHJhdyBtLVBhdGggZGF0YSB3aXRoIG1wYXRociIKZGF0ZTogImByIFN5cy5EYXRlKClgIgphdXRob3I6ICJMYXJhIE5hdmFycmV0ZSBhbmQgS29lbiBOaWVtZWlqZXIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGNlcnVsZWFuCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICBzbWFydDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogIjIiCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93IgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9jb3B5OiB0cnVlCiAgICBkZl9wcmludDogdGliYmxlCiAgICBjc3M6IHN0eWxlcy5jc3MKICAgIGluY2x1ZGVzOgogICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNhY2hlID0gRkFMU0UsCiAgaGlnaGxpZ2h0ID0gVFJVRSwKICBmaWcud2lkdGggPSA5LAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIGNvbW1lbnQgPSAiIz4iLAogIHRpZHkgPSAic3R5bGVyIiwKICB0aWR5Lm9wdHMgPSBsaXN0KAogICAgYXJyb3cgPSBUUlVFLAogICAgaW5kZW50ID0gMiwKICAgIHdpZHRoLmN1dG9mZiA9IDgwCiAgKQopCmBgYAoKYGBge2NzcywgZWNobz1GQUxTRX0KLnNjcm9sbGFibGUgewogIG1heC1oZWlnaHQ6IDUwMHB4OwogIG92ZXJmbG93LXk6IGF1dG87CiAgYmFja2dyb3VuZC1jb2xvcjogaW5oZXJpdDsKfQpgYGAKCgpUaGlzIGd1aWRlIGltcGxlbWVudHMgdGhlIHByZXByb2Nlc3Npbmcgb2YgYW4gZXhhbXBsZSBkYXRhc2V0IGdhdGhlcmVkIHdpdGggW20tUGF0aF0oaHR0cHM6Ly9tLVBhdGguaW8vbGFuZGluZy8pIGFuZCBsb2FkZWQgd2l0aCBpdHMgY29tcGFuaW9uIFIgcGFja2FnZSBgbXBhdGhyYC4gbS1QYXRoIGlzIGEgcGxhdGZvcm0gdGhhdCBhbGxvd3MgcmVzZWFyY2hlcnMgdG8gY3JlYXRlIGFuZCBkZXBsb3kgRXhwZXJpZW5jZSBTYW1wbGluZyBNZXRob2QgKEVTTSkgc3R1ZGllcy4gRVNNIHN0dWRpZXMgYXJlIGEgdHlwZSBvZiBsb25naXR1ZGluYWwgc3R1ZHkgd2hlcmUgcGFydGljaXBhbnRzIGFyZSBwcm9tcHRlZCB0byBhbnN3ZXIgcXVlc3Rpb25zIGFib3V0IHRoZWlyIGVtb3Rpb25zLCBhY3Rpdml0aWVzLCBhbmQgY29udGV4dCBpbiByZWFsLXRpbWUuIAoKVGhlIGRhdGFzZXQgY29uc2lzdHMgb2YgdGhlIHJlc3VsdGluZyBkYXRhIGZyb20gYW4gRVNNIGV4cGVyaW1lbnQsIGZyb20gMjAgcGFydGljaXBhbnRzIGNvbGxlY3RlZCBvdmVyIHRoZSBjb3Vyc2Ugb2YgMTAgZGF5cy4gUGFydGljaXBhbnRzIGNvbXBsZXRlZCB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ubmFpcmVzOgoKKiBJbnRha2UgcXVlc3Rpb25uYWlyZTogd2hlcmUgcGFydGljaXBhbnRzIGNvbnNlbnRlZCB0byBwYXJ0aWNpcGF0ZSBhbmQgZmlsbGVkIHRoZSBOZXVyb3RpY2lzbSBzdWJzY2FsZSBvZiB0aGUgQmlnIEZpdmUgSW52ZW50b3J5IChCRkkpIGFuZCB0aGUgU2F0aXNmYWN0aW9uIHdpdGggTGlmZSBTY2FsZSAoU1dMUykuCiogTWFpbiBxdWVzdGlvbm5haXJlIChhZG1pbmlzdGVyZWQgMTAgdGltZXMgcGVyIGRheSk6IFBhcnRpY2lwYW50cyBhbnN3ZXJlZCBxdWVzdGlvbnMgYWJvdXQgdGhlaXIgZW1vdGlvbnMgYW5kIGFjdGl2aXRpZXMuCiogRXZlbmluZyBxdWVzdGlvbm5haXJlIChldmVyeSBldmVuaW5nKTogd2hlcmUgcGFydGljaXBhbnRzICByYXRlZCB0aGVpciBlbW90aW9ucyB0aHJvdWdob3V0IHRoZSBkYXkuCgpUaGUgZGF0YXNldCBpcyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgYG1wYXRoX2V4YW1wbGUoKWAgZnVuY3Rpb24gaW4gYG1wYXRocmAgYnV0IGNhbiBhbHNvIGJlIGRvd25sb2FkZWQgc2VwYXJhdGVseSBmcm9tIEdpdGh1YiBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL20tcGF0aC1pby9tcGF0aHIvdHJlZS9tYXN0ZXIvaW5zdC9leHRkYXRhKS4KClRoaXMgZ3VpZGUgYnVpbGRzIHVwb24gdGhlIGZyYW1ld29yaywgUiB0dXRvcmlhbHMsIGFuZCB0ZW1wbGF0ZXMgZm9yIHByZXByb2Nlc3NpbmcgRVNNIGRhdGEgZGV2ZWxvcGVkIGJ5IFJldm9sIGV0IGFsLiAoMjAyNCkuIFRoZXNlIHJlc291cmNlcyBhcmUgZGV0YWlsZWQgaW4gdGhlIGFzc29jaWF0ZWQgW3BhcGVyXShodHRwczovL2pvdXJuYWxzLnNhZ2VwdWIuY29tL2RvaS9mdWxsLzEwLjExNzcvMjUxNTI0NTkyNDEyNTY2MDkpIGFuZCB0aGUgW0VTTSBQcmVwcm9jZXNzaW5nIEdhbGxlcnldKGh0dHBzOi8vcHJlcHJvY2Vzcy5lc210b29scy5jb20vKSB3ZWJzaXRlLiBXaGVuIHVzaW5nIHRoaXMgd29yaywgcGxlYXNlIGNpdGUgdGhlIGZvbGxvd2luZyBwYXBlcjogCgoqIFJldm9sLCBKLiwgQ2FybGllciwgQy4sIExhZml0LCBHLiwgVmVyaGVlcywgTS4sIFNlbHMsIEwuLCAmIENldWxlbWFucywgRS4gKDIwMjQpLiBQcmVwcm9jZXNzaW5nIEV4cGVyaWVuY2UtU2FtcGxpbmctTWV0aG9kIERhdGE6IEEgU3RlcC1ieS1TdGVwIEZyYW1ld29yaywgVHV0b3JpYWwgV2Vic2l0ZSwgUiBQYWNrYWdlLCBhbmQgUmVwb3J0aW5nIFRlbXBsYXRlcy4gX0FkdmFuY2VzIGluIE1ldGhvZHMgYW5kIFByYWN0aWNlcyBpbiBQc3ljaG9sb2dpY2FsIFNjaWVuY2VfLCA3KDQpLCAx4oCTMTguIGh0dHBzOi8vZG9pLm9yZy8xMC4xMTc3LzI1MTUyNDU5MjQxMjU2NjA5LgoKCiMjIFNldCB1cAoKV2Ugd2lsbCBzdGFydCBieSBsb2FkaW5nIHRoZSBwYWNrYWdlcyB0aGF0IHdlIHdpbGwgdXNlIGluIHRoaXMgYW5hbHlzaXMuIFNpbmNlIHRoaXMgZ3VpZGUgcmVxdWlyZXMgdGhlIGluc3RhbGxhdGlvbiBvZiBhIGdvb2QgbnVtYmVyIG9mIHBhY2thZ2VzLCB0aGUgZm9sbG93aW5nIGNvZGUgYXV0b21hdGljYWxseSBpbnN0YWxscyBwYWNrYWdlcyB0aGF0IGFyZSBub3QgYWxyZWFkeSBpbnN0YWxsZWQuIFBhY2thZ2VzIGFyZSBsb2FkZWQgd2hlbmV2ZXIgdGhlc2UgYXJlIG5lZWRlZCBpbiB0aGUgZ3VpZGUuCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcmVxdWlyZWRfcGFja2FnZXMgPC0gYygKICAibXBhdGhyIiwgImVzbXRvb2xzIiwgInNraW1yIiwgImRwbHlyIiwgInRpZHlyIiwgInZpc2RhdCIsICJsdWJyaWRhdGUiLCAiZ2dwbG90MiIsIAogICJnZ2xhbHQiLCAic2NhbGVzIiwgImxtZTQiLCAicGVyZm9ybWFuY2UiLCAiY2FyIiwgIm1vZGVsYmFzZWQiLCAicnNxIgopCgojIGNoZWNrcyB3aGljaCBvZiB0aGUgcGFja2FnZXMgYXJlIGluc3RhbGxlZCBhbmQgaW5zdGFsbHMgdGhlIG9uZXMgdGhhdCBhcmUgbm90Cmluc3RhbGwucGFja2FnZXMoc2V0ZGlmZihyZXF1aXJlZF9wYWNrYWdlcywgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSkKCiMgY3JlYXRlIG0tUGF0aCBwYWxldHRlcyAoZm9yIHBsb3R0aW5nKToKZ2dwbG90Mjo6dGhlbWVfc2V0KGdncGxvdDI6OnRoZW1lX2J3KCkpCgojIENvbG9ycyBmb3IgdGhlIHBsb3RzIGluIHRoaXMgZ3VpZGUKbXBhdGhfcGFsZXR0ZSA8LSBjKCIjMjE5NkYzIiwgIiNGMzM5MjAiLCAiI0Y0QTIyMiIpCmRhcmtfbXBhdGhfcGFsZXR0ZSA8LSBjKCIjMDg3RERBIiwgIiNEQTIwMDYiLCAiI0RCODkwOCIpCmBgYAoKIyBTdGVwIDE6IEltcG9ydGluZyBkYXRhIGFuZCBwcmVsaW1pbmFyeSBwcmVwcm9jZXNzaW5nCgpJbiB0aGlzIHN0ZXAsIHdlIHdpbGwgaW1wb3J0IHRoZSBkYXRhIGFuZCBwZXJmb3JtIHNvbWUgcHJlbGltaW5hcnkgcHJlcHJvY2Vzc2luZy4gV2UgZmlyc3QgdGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEsIGFuZCB0aGVuIHdlIGNsZWFuIGl0IGJ5IHJlbmFtaW5nIGNvbHVtbnMgYW5kIG1ha2luZyBjb21wb3NpdGUgdmFyaWFibGVzLiBXZSB0aGVuIGNoZWNrIGZvciBkdXBsaWNhdGVkIGFuZCBtaXNzaW5nIHZhcmlhYmxlcywgYW5kIGZpbmFsbHkgcmVmb3JtYXQgdGhlIHRpbWUgc3RhbXBzIGFuZCBjcmVhdGUgbmV3IHRpbWUgdmFyaWFibGVzLgoKIyMgSW1wb3J0aW5nClRvIHJlYWQgdGhlIGBiYXNpYy5jc3ZgIGFzIGV4cG9ydGVkIGZyb20gbS1QYXRoLCB3ZSBjYW4gdXNlIHRoZSBgcmVhZF9tcGF0aCgpYCBmdW5jdGlvbiwgdGhhdCB0YWtlcyBhcyBhcmd1bWVudHMgdGhlIHBhdGggdG8gdGhlIGRhdGEgKGBmaWxlYCkgYW5kIHRoZSBwYXRoIHRvIHRoZSBtZXRhIGRhdGEgKGBtZXRhX2RhdGFgKSwgYW5kIHJldHVybnMgdGhlIGRhdGEgYXMgYSBkYXRhIGZyYW1lLiBUaGUgbWV0YWRhdGEgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgaG93IHRoZSBkYXRhIHNob3VsZCBiZSByZWFkLCB0aGlzIGluZm9ybWF0aW9uIGlzIHVzZWQgYnkgdGhlIGByZWFkX21wYXRoKClgIGZ1bmN0aW9uIHRvIGdldCB0aGUgY29ycmVjdCBkYXRhIHR5cGVzLiBJbiB0aGlzIGd1aWRlLCB3ZSB3aWxsIHVzZSB0aGUgZXhhbXBsZSBkYXRhIHRoYXQgY29tZXMgd2l0aCB0aGUgYG1wYXRocmAgcGFja2FnZSwgd2hpY2ggY2FuIGJlIGZvdW5kIGJ5IGNhbGxpbmcgYG1wYXRoX2V4YW1wbGUoKWAuCgpgYGB7ciByZWFkIG0tUGF0aCBkYXRhfQpsaWJyYXJ5KG1wYXRocikKCmRhdGEgPC0gcmVhZF9tcGF0aCgKICBmaWxlID0gbXBhdGhfZXhhbXBsZSgiZXhhbXBsZV9iYXNpYy5jc3YiKSwgIyBwYXRoIHRvIHRoZSBkYXRhIAogIG1ldGFfZGF0YSA9IG1wYXRoX2V4YW1wbGUoImV4YW1wbGVfbWV0YS5jc3YiKSAjIHBhdGggdG8gdGhlIG1ldGFkYXRhCikgCmBgYAoKIyMgTm90ZTogc2F2aW5nIG0tUGF0aCBkYXRhCgpUaGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgd2lsbCBjb250YWluIGNvbHVtbnMgd2l0aCBsaXN0cywKd2hpY2ggY2FuIGJlIHByb2JsZW1hdGljIHdoZW4gc2F2aW5nIHRoZSBkYXRhLiBUbyBzYXZlIHRoZSBkYXRhLCB3ZSBzdWdnZXN0IHRoZQpmb2xsb3dpbmcgdHdvIG9wdGlvbnM6CgpJZiB5b3Ugd2FudCB0byBzYXZlIHRoZSBkYXRhIGFzIGEgY29tbWEtc2VwYXJhdGVkIHZhbHVlcyAoQ1NWKSBmaWxlIHRvIHVzZSBpdCBpbiBhbm90aGVyIHByb2dyYW0sIAp1c2UgYHdyaXRlX21wYXRoKClgLiBUaGlzIGZ1bmN0aW9uIHdpbGwgY29sbGFwc2UgbW9zdCBsaXN0IGNvbHVtbnMgdG8gYSBzaW5nbGUgc3RyaW5nIGFuZCBwYXJzZXMKYWxsIGNoYXJhY3RlciBjb2x1bW5zIHRvIEpTT04gc3RyaW5ncywgZXNzZW50aWFsbHkgcmV2ZXJzaW5nIHRoZSBvcGVyYXRpb25zIHBlcmZvcm1lZCBieSAKYHJlYWRfbXBhdGgoKWAuIE5vdGUgdGhhdCB0aGlzIGRvZXMgbm90IG1lYW4gdGhhdCBkYXRhIGNhbiBiZSByZWFkIGJhY2sgdXNpbmcgYHJlYWRfbXBhdGgoKWAsCmJlY2F1c2UgdGhlIGRhdGEgbWF5IGhhdmUgYmVlbiBtb2RpZmllZCBhbmQgdGh1cyBubyBsb25nZXIgYmUgaW4gbGluZSB3aXRoIHRoZSBtZXRhIGRhdGEuCgpgYGB7ciB3cml0ZSBkYXRhIGFzIGNzdiwgZXZhbCA9IEZBTFNFfQp3cml0ZV9tcGF0aCgKICB4ID0gZGF0YSwKICBmaWxlID0gImRhdGEuY3N2IgopCmBgYAoKT3RoZXJ3aXNlLCBpZiB0aGUgZGF0YSB3aWxsIGJlIHVzZWQgZXhjbHVzaXZlbHkgaW4gUiwgd2Ugc3VnZ2VzdCBzYXZpbmcgaXQgYXMgYW4gUiBvYmplY3QgKGAuUkRhdGFgIApvciBgLlJEU2ApOgpgYGB7ciB3cml0ZSBkYXRhIGFzIGFuIFIgb2JqZWN0LCBldmFsID0gRkFMU0V9CiMgQXMgYW4gLlJEYXRhIGZpbGUuIFdoZW4gdXNpbmcgYGxvYWQoKWAsIG5vdGUgdGhhdCB0aGUgZGF0YSB3aWxsIGJlIHN0b3JlZCBpbiB0aGUgYGRhdGFgCiMgb2JqZWN0IGluIHRoZSBnbG9iYWwgZW52aXJvbm1lbnQuCnNhdmUoCiAgZGF0YSwgCiAgZmlsZSA9ICJkYXRhLlJEYXRhIgopCgojIEFzIGFuIFJEUyBmaWxlLgpzYXZlUkRTKAogIGRhdGEsIAogIGZpbGUgPSAiZGF0YS5SRFMiCikKYGBgCgojIyBGaXJzdCBnbGltcHNlCldlIGNhbiBmaXJzdCBjaGVjayB0aGUgZGF0YSB0byBnZXQgYSBiZXR0ZXIgaWRlYSBvZiB3aGF0IHdlIGFyZSB3b3JraW5nIHdpdGguIAoKKipNZXRhLWluZm8gb2YgdGhlIEVTTSBkYXRhc2V0LioqIApUaGUgYGRhdGFJbmZvKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBlc210b29sc2AgcGFja2FnZSBwcm92aWRlcyBzb21lIHVzZWZ1bCBpbmZvcm1hdGlvbiwgc3VjaCBhcyB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgYW5kIHJvd3MsIHRoZSBudW1iZXIgb2YgcGFydGljaXBhbnRzLCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBwZXIgcGFydGljaXBhbnQsIGFuZCB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzLgoKYGBge3IgcHJpbnQgZGF0YSBpbmZvLCBjbGFzcy5vdXRwdXQ9InNjcm9sbGFibGUifQpsaWJyYXJ5KGVzbXRvb2xzKQoKZGF0YUluZm8oCiAgZmlsZV9wYXRoID0gbXBhdGhfZXhhbXBsZSgiZXhhbXBsZV9iYXNpYy5jc3YiKSwKICByZWFkX2Z1biA9IHJlYWQuY3N2MiwKICBpZHZhciA9ICJjb25uZWN0aW9uSWQiLCAjIHNwZWNpZnkgdGhlIHBhcnRpY2lwYW50IHZhcmlhYmxlCiAgdGltZXZhciA9ICJ0aW1lU3RhbXBTZW50IiAjIHNwZWNpZnkgdGhlIHRpbWUgdmFyaWFibGUKKQpgYGAKV2UgY2FuIHNlZSB0aGF0IHdlIGhhdmUgYHIgbGVuZ3RoKHVuaXF1ZShkYXRhJGNvbm5lY3Rpb25JZCkpYCBwYXJ0aWNpcGFudHMsIGFuZCB3ZSBoYXZlIGByIG5yb3coZGF0YSkvbGVuZ3RoKHVuaXF1ZShkYXRhJGNvbm5lY3Rpb25JZCkpYCBvYnNlcnZhdGlvbnMgcGVyIHBhcnRpY2lwYW50LgoKV2UgY2FuIGFsc28gcHJpbnQgdGhlIGRhdGEgYW5kIHVzZSB0aGUgYHNraW0oKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHNraW1yYCBwYWNrYWdlIHRvIGdldCBhIHN1bW1hcnkgb2YgdGhlIGRhdGEuCgpgYGB7ciBtb2RpZnkgcHJpbnQuc2tpbV9kZiwgZWNobyA9IEZBTFNFfQpwcmludC5za2ltX2RmIDwtIGZ1bmN0aW9uKHgsIC4uLikgewogIHNraW1yOjo6cHJpbnQuc2tpbV9kZih4LCB3aWR0aCA9IDkwLCBuID0gMTApCn0KYGBgCgpgYGB7ciBwcmludCBkYXRhIGFuZCBza2ltfQpsaWJyYXJ5KHNraW1yKQoKaGVhZChkYXRhKSAjIHByaW50cyBmaXJzdCByb3dzIG9mIGRmCgpwcmludChza2ltKGRhdGEpKSAjIHByaW50cyBzdW1tYXJ5IG9mIGRmCmBgYApGcm9tIHRoZSBvdXRwdXQgb2YgdGhlIGBza2ltYCBmdW5jdGlvbiwgd2UgY2FuIHNlZSB0aGF0IHdlIGRvbiJ0IGhhdmUgYW55IG1pc3NpbmcgdmFsdWVzIGluIGBjb25uZWN0aW9uSWRgIChvdXIgcGFydGljaXBhbnQgaWRlbnRpZmllciB2YXJpYWJsZSksIGFuZCBhbHNvIGluIHRoZSB0aW1lIHZhcmlhYmxlIGB0aW1lU3RhbXBTZW50YC4gQnV0IHdlIGhhdmUgYSBsb3Qgb2YgbWlzc2luZyB2YWx1ZXMgaW4gb3RoZXIgdmFyaWFibGVzLiBUaGlzIGlzIGJlY2F1c2UgZWFjaCByb3cgY29ycmVzcG9uZHMgdG8gb25lIGJlZXAsIGFuZCBzaW5jZSB0aGUgZGF0YSBjb25zaXN0cyBvZiB0aHJlZSBxdWVzdGlvbm5haXJlcywgbm90IGFsbCBxdWVzdGlvbnMgYXJlIGFuc3dlcmVkIGluIGVhY2ggYmVlcC4KCiMjIEluaXRpYWwgY2xlYW5pbmcgYW5kIGNvbHVtbiByZW5hbWluZwpCZWZvcmUgY29udGludWluZyB3aXRoIHRoZSBwcmUtcHJvY2Vzc2luZywgd2Ugd2lsbCBkbyBzb21lIGluaXRpYWwgY2xlYW5pbmcgYW5kIG1ha2UgdGhlIGNvbHVtbnMgbW9yZSByZWFkYWJsZS4KYGBge3IgcHJpbnQgY29sbmFtZXMsIGNsYXNzLm91dHB1dD0ic2Nyb2xsYWJsZSJ9CmNvbG5hbWVzKGRhdGEpICMgcHJpbnRzIHRoZSBjb2x1bW4gbmFtZXMKYGBgCgpUaGVyZSBhcmUgc29tZSBjb2x1bW5zIHRoYXQgd2Ugd2lsbCBub3QgbmVlZCBmb3Igb3VyIGFuYWx5c2lzIChidXQgbWF5IGNvbnRhaW4gdXNlZnVsIGluZm9ybWF0aW9uIGluIG90aGVyIGNhc2VzKS4gV2UgY2FuIGFscmVhZHkgcmVtb3ZlIHRoZW0gYnkgdXNpbmcgdGhlIGBzZWxlY3QoKWAgZnVuY3Rpb24gaW4gdGhlIGBkcGx5cmAgcGFja2FnZS4gSWYgd2UgdXNlIHRoZSBgLWAgb3IgYCFgIHNpZ24gYmVmb3JlIGxpc3RpbmcgdGhlIGNvbHVtbnMsIHdlIHNlbGVjdCBldmVyeXRoaW5nICoqZXhjZXB0KiogdGhvc2UgY29sdW1uczoKCmBgYHtyIHJlbW92ZSBjb2x1bW5zLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoZHBseXIpCgojIFdlIHByb3ZpZGUgdGhlIGZ1bmN0aW9uIHdpdGggYSB2ZWN0b3Igd2l0aCB0aGUgY29sdW1uIG5hbWVzIHdlIHdhbnQgdG8gcmVtb3ZlCmRhdGEgPC0gZGF0YSB8PiAKICBzZWxlY3QoLWMoCiAgICAibGVnYWN5Q29kZSIsICJhbGlhcyIsICJpbml0aWFscyIsICJzY2hlZHVsZWRCZWVwSWQiLCAic2VudEJlZXBJZCIsICJhY2NvdW50Q29kZSIsIAogICAgInJlbWluZGVyRm9yT3JpZ2luYWxTZW50QmVlcElkIiwgIm9yaWdpbmFsVGltZVN0YW1wU2VudCIsICJxdWVzdGlvbkxpc3RMYWJlbCIsIAogICAgInRpbWVab25lT2Zmc2V0IiwgImZyb21Qcm90b2NvbE5hbWUiLCAiU1dMU19pbnRyb19iYXNpYyIsIAogICAgIkJGSV9uZXVyb3RpY2lzbV9pbnRyb19iYXNpYyIsICJpbnRha2Vfb3V0cm9fYmFzaWMiLCAibWFpbl9vdXRyb19iYXNpYyIsCiAgICAiZXZlbmluZ19xdWVzdGlvbm5haXJlX3dhcm5pbmdfYmFzaWMiLCAiZXZlbmluZ19vdXRyb19iYXNpYyIKICApKSAKYGBgCgpOb3RlOiBpbiB0aGlzIGNhc2UsIHdlIGFyZSByZW1vdmluZyB0aGUgY29sdW1uIGBvcmlnaW5hbFRpbWVTdGFtcFNlbnRgIGJlY2F1c2UgaW4gb3VyIGV4cGVyaW1lbnQsIHdlIGRpZCBub3QgaGF2ZSByZW1pbmRlcnMuIEluIGNhc2Ugb2YgcmVtaW5kZXJzLCB0aGUgYG9yaWdpbmFsVGltZVN0YW1wU2VudGAgY29udGFpbnMgdGhlIHRpbWUgc3RhbXAgZm9yIHRoZSBmaXJzdCBub3RpZmljYXRpb24gYW5kIGB0aW1lU3RhbXBTZW50YCBjb250YWlucyB0aGUgdGltZSBzdGFtcCBmb3IgdGhlIHJlbWluZGVyLiAKCk9uY2Ugd2UgaGF2ZSByZW1vdmVkIHNvbWUgY29sdW1ucywgd2UgY2FuIHJlbmFtZSB0aGUgcmVzdC4gU2luY2Ugd2UgYXJlIHdvcmtpbmcgd2l0aCBgciBuY29sKGRhdGEpYCBjb2x1bW5zLCBjaGFuZ2luZyBlYWNoIGNvbHVtbiBuYW1lIG1hbnVhbGx5IHdvdWxkIGJlIHZlcnkgdGltZS1jb25zdW1pbmcuIAoKVG8gYXZvaWQgZG9pbmcgdGhpcywgd2UgbWF5IHdhbnQgdG8gY2hlY2sgdGhlIGNvbHVtbiBuYW1lcyBhbmQgc2VlIGlmIHRoZXJlIGlzIGEgcGF0dGVybiB0aGF0IHdlIGNhbiB1c2UgdG8gbWFrZSBvdXIgdGFzayBlYXNpZXIuIEluIHRoaXMgY2FzZSwgc2luY2Ugb3VyIHF1ZXN0aW9ubmFpcmUgd2FzIGltcGxlbWVudGVkIGluIG0tUGF0aCwgdGhlIGNvbHVtbiBuYW1lcyBjb25zaXN0IG9mIHRoZSBsYWJlbCB3ZSBnYXZlIGluIHF1ZXN0aW9ubmFpcmUgY3JlYXRpb24sIGFzIHdlbGwgYXMgYSBzdWZmaXggaW5kaWNhdGVkIHRoZSB0eXBlIG9mIHF1ZXN0aW9uIChGb3IgZXhhbXBsZTogYGFnZV9vcGVuYCBoYXMgdGhlIHN1ZmZpeCBgX29wZW5gKS4gS25vd2luZyB0aGlzLCB3ZSBjYW4gcmVtb3ZlIHNvbWUgb2YgdGhlc2Ugc3VmZml4ZXMgaW4gYWxsIHRoZSBjb2x1bW5zIHRoYXQgaGF2ZSB0aGVtLgoKQW4gZXhhbXBsZSBvZiB0aGlzIGlzIHRoZSBNdWx0aXBsZSBjaG9pY2UgcXVlc3Rpb25zICh0aGF0IGVuZCBpbiBgX211bHRpcGxlQ2hvaWNlYCkuIFdlIGNhbiByZW1vdmUgdGhlc2UgdXNpbmcgdGhlIGBnc3ViKClgIGZ1bmN0aW9uLiBJbiB0aGlzIGZ1bmN0aW9uLCB3ZSBzdXBwbHkgdGhlIGNvbHVtbiBuYW1lcywgc3BlY2lmeSB0aGUgcGF0dGVybiB3ZSB3YW50IHRvIHJlcGxhY2UgKGBfbXVsdGlwbGVDaG9pY2VgKSwgYW5kIHRoZSByZXBsYWNlbWVudCAoYCIiYCwgYW4gZW1wdHkgc3RyaW5nKS4KCmBgYHtyIHJlbmFtZSBjb2x1bW5zIHVzaW5nIGdzdWJ9CnBhdHRlcm5zIDwtIGMoCiAgIl95ZXNubyQiLCAjIHJlbW92ZXMgX3llc25vLCBidXQgb25seSBpZiBpdCBpcyBhdCB0aGUgZW5kIG9mIHRoZSBzdHJpbmcgKHVzaW5nICQpIAogICJfbXVsdGlwbGVDaG9pY2UiLCAjIFJlbW92ZSBfbXVsdGlwbGVDaG9pY2Ugc3VmZml4CiAgIl9zbGlkZXJOLioiLCAjIFJlbW92ZSBfc2xpZGVyIHN1ZmZpeGVzIGFuZCBldmVyeXRoaW5nIGFmdGVyIGl0CiAgIl9vcGVuIiwgIyBSZW1vdmUgX29wZW4KICAiY29udGV4dF8iICMgUmVtb3ZlIGNvbnRleHRfIAopCgojIE1lcmdlIHRoZW0gaW50byBvbmUgc3RyaW5nIHRoYXQgd2UgY2FuIHVzZSBhIHJlZ3VsYXIgZXhwcmVzc2lvbgpwYXR0ZXJucyA8LSBwYXN0ZTAoIigiLCBwYXN0ZShwYXR0ZXJucywgY29sbGFwc2UgPSAifCIpLCAiKSIpCgpjb2xuYW1lcyhkYXRhKSA8LSBnc3ViKAogIHBhdHRlcm4gPSBwYXR0ZXJucywgCiAgcmVwbGFjZW1lbnQgPSAiIiwKICB4ID0gY29sbmFtZXMoZGF0YSkKKQpgYGAKCioqV2FybmluZyoqOiBjbGVhbmluZyBjb2x1bW4gbmFtZXMgaW4gdGhpcyB3YXkgbWF5IHJlc3VsdCBpbiBkdXBsaWNhdGVkIGNvbHVtbiBuYW1lcyBGb3IgZXhhbXBsZTogaWYgdGhlcmUgd2FzIGEgY29sdW1uIGNhbGxlZCBgc2xpZGVyX2hhcHB5X3NsaWRlck5lZ1Bvc2AsIHdlIHdvdWxkIGVuZCB1cCB3aXRoIHR3byBjb2x1bW5zIG5hbWVkIGBzbGlkZXJfaGFwcHlgLCBiZWNhdXNlIG9mIHRoZSBjb2x1bW4gYHNsaWRlcl9oYXBweV9zbGlkZXJOZXV0cmFsUG9zYC4gVGhpcyBjYW4gYmUgYSBwcm9ibGVtLCBzbyBpdCBpcyBpbXBvcnRhbnQgdG8gYmUgYXdhcmUgb2YgdGhpcyBhbmQgbWF5YmUgY2hlY2sgZm9yIGR1cGxpY2F0ZWQgY29sdW1uIG5hbWVzIGFmdGVyIGNsZWFuaW5nIHRoZW0uCgpgYGB7ciBjaGVjayBmb3IgZHVwbGljYXRlZCBjb2x1bW4gbmFtZXN9CiMgY2hlY2tpbmcgZm9yIGR1cGxpY2F0ZWQgY29sdW1uIG5hbWVzCmFueUR1cGxpY2F0ZWQobmFtZXMoZGF0YSkpCmBgYApXZSBoYXZlIG5vIGR1cGxpY2F0ZWQgY29sdW1uIG5hbWVzIGFzIGEgcmVzdWx0IG9mIHRoZSBwcmV2aW91cyByZW5hbWluZy4KCi0tLS0KCk5vdywgd2UgaGF2ZSByZW1vdmVkIHNvbWUgc3VmZml4ZXMgYW5kIG91ciBjb2x1bW4gbmFtZXMgYXJlIGxlc3MgY2x1dHRlcmVkLiBXZSBjYW4gcmVuYW1lIHRoZSByZXN0IG9mIHRoZSBjb2x1bW5zIG1hbnVhbGx5IHdpdGggYHJlbmFtZSgpYC4gTm90ZSB0aGF0IHRoaXMgaXMgbm90IG5lY2Vzc2FyeSwgYnV0IHdlIHdpbGwgdXNlIHRoZXNlIHJlbmFtaW5nIHN0YW5kYXJkcyBpbiB0aGlzIGd1aWRlLgoKYGBge3IgcmVuYW1pbmcgY29sdW1ucyBtYW51YWxseX0KZGF0YSA8LSBkYXRhIHw+IAogIHJlbmFtZSgKICAgIHBhcnRpY2lwYW50ID0gImNvbm5lY3Rpb25JZCIsICMgcmVuYW1pbmcgY29ubmVjdGlvbklkIHRvIHBhcnRpY2lwYW50CiAgICBnZW5kZXIgPSAiZ2VuZGVyX2luZGV4IiwKICAgIHF1ZXN0aW9ubmFpcmUgPSAicXVlc3Rpb25MaXN0TmFtZSIsCiAgICBzY2hlZHVsZWQgPSAidGltZVN0YW1wU2NoZWR1bGVkIiwKICAgIHNlbnQgPSAidGltZVN0YW1wU2VudCIsCiAgICBzdGFydCA9ICJ0aW1lU3RhbXBTdGFydCIsCiAgICBzdG9wID0gInRpbWVTdGFtcFN0b3AiLAogICAgc3RlcF9jb3VudCA9ICJzdGVwX2NvdW50X3N0ZXBzIiwKICAgIHBob25lX3NlcnZlcl9vZmZzZXQgPSAiZGVsdGFVVEMiLAogICAgZXZlbmluZ19zdHJlc3NmdWwgPSAiZXZlbmluZ195ZXNub19zdHJlc3NmdWwiLAogICAgZXZlbmluZ19wb3NpdGl2ZSA9ICJldmVuaW5nX3llc25vX3Bvc2l0aXZlIgogICkKYGBgCgojIyMgUmVwbGFjZSBwYXJ0aWNpcGFudCB2YWx1ZXMgd2l0aCBhIHBhcnRpY2lwYW50IG51bWJlcgpPdXIgcGFydGljaXBhbnQgaWRlbnRpZmllciBpcyB0aGUgY29sdW1uIGBwYXJ0aWNpcGFudGAsIHdoaWNoIGNvbnRhaW5zIElEcyBzdWNoIGFzIGByIHVuaXF1ZShkYXRhJHBhcnRpY2lwYW50KVsxXWAuIFdlIGNhbiByZXBsYWNlIHRoZXNlIElEcyB3aXRoIGEgcGFydGljaXBhbnQgbnVtYmVyIHRoYXQgZ29lcyBmcm9tIDEgdG8gdGhlIG51bWJlciBvZiBwYXJ0aWNpcGFudHMuIAoKQnV0IGZpcnN0LCB3ZSBzaG91bGQgY2hlY2sgdGhhdCB0aGVyZSBhcmUgbm8gYE5BYHMgaW4gdGhpcyBjb2x1bW46CmBgYHtyIENoZWNrIGZvciBOQXMgaW4gcGFydGljaXBhbnQgY29sdW1ufQojIGZpcnN0IGNoZWNrIHRoYXQgdGhlcmUgYXJlIG5vIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBwYXJ0aWNpcGFudCB2YXJpYWJsZQphbnlOQShkYXRhJHBhcnRpY2lwYW50KQpgYGAKClRoZXJlIGFyZSBubyBgTkFgcyBpbiB0aGUgcGFydGljaXBhbnQgdmFyaWFibGUsIHNvIHdlIGNhbiBzYWZlbHkgcmVwbGFjZSBpdCB3aXRoIGEgcGFydGljaXBhbnQgbnVtYmVyLiBXZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgbWF0Y2goKWAgdG8gcmVwbGFjZSB0aGUgcGFydGljaXBhbnQgYWxpYXMgd2l0aCBpdHMgaW5kZXggaW4gYHVuaXF1ZShkYXRhJHBhcnRpY2lwYW50KWAuIFRoaXMgd2lsbCB5aWVsZCB1cyB3aXRoIHBhcnRpY2lwYW50IG51bWJlcnMgdGhhdCBnbyBmcm9tIDEgdG8gdGhlIG51bWJlciBvZiBwYXJ0aWNpcGFudHMgKHdoaWNoIGlzIGBsZW5ndGgodW5pcXVlKGRhdGEkcGFydGljaXBhbnQpKWApLgoKYGBge3IgcmVwbGFjZSBwYXJ0aWNpcGFudCB2YWx1ZXMgd2l0aCBwYXJ0aWNpcGFudCBudW1iZXJ9CiMgcHJpbnQgdW5pcXVlIHZhbHVlcyBpbiBkYXRhJHBhcnRpY2lwYW50IGJlZm9yZSBtYXRjaAp1bmlxdWUoZGF0YSRwYXJ0aWNpcGFudCkgCgojIG1hdGNoZXMgdGhlIHZhbHVlcyBpbiBkYXRhJHBhcnRpY2lwYW50IHdpdGggdGhlIHVuaXF1ZSB2YWx1ZXMgaW4gZGF0YSRwYXJ0aWNpcGFudCBhbmQKIyByZXR1cm5zIHRoZSBpbmRleCBvZiB0aGUgbWF0Y2gKZGF0YSRwYXJ0aWNpcGFudCA8LSBtYXRjaChkYXRhJHBhcnRpY2lwYW50LCB1bmlxdWUoZGF0YSRwYXJ0aWNpcGFudCkpIAoKIyBwcmludCB1bmlxdWUgdmFsdWVzIGluIGRhdGEkcGFydGljaXBhbnQgYWZ0ZXIgbWF0Y2gKdW5pcXVlKGRhdGEkcGFydGljaXBhbnQpIApgYGAKCiMjIE1ha2luZyBjb21wb3NpdGUgdmFyaWFibGVzCkFmdGVyIGhhdmluZyBkb25lIHNvbWUgaW5pdGlhbCBjbGVhbmluZywgd2UgY2FuIGNyZWF0ZSBjb21wb3NpdGUgdmFyaWFibGVzLiBJbiBvdXIgc3R1ZHksIHBhcnRpY2lwYW50cyBjb21wbGV0ZWQgYSBgbWFpbl9xdWVzdGlvbm5haXJlYCwgYW4gYGV2ZW5pbmdfcXVlc3Rpb25uYWlyZWAgYW5kIGEgYENvbnNlbnQgYW5kIGludGFrZSBxdWVzdGlvbm5haXJlYC4gSW4gdGhpcyBsYXN0IG9uZSwgcGFydGljaXBhbnRzIHByb3ZpZGVkIGluZm9ybWF0aW9uIHRoYXQgZG9lcyBub3QgY2hhbmdlIG92ZXIgdGltZS4KCmBgYHtyIHByaW50IGRpZmZlcmVudCBxdWVzdGlvbm5haXJlcyBpbiB0aGUgc3R1ZHl9CiMgdG8gY2hlY2sgd2hpY2ggZGlmZmVyZW50IHF1ZXN0aW9ubmFpcmVzIGFyZSBwYXJ0IG9mIHRoZSBkYXRhc2V0Ogp1bmlxdWUoZGF0YSRxdWVzdGlvbm5haXJlKSAKYGBgCkluIHRoZSBpbnRha2UgcXVlc3Rpb25uYWlyZSwgcGFydGljaXBhbnRzIHdlcmUgYXNrZWQgYWJvdXQ6IAoKMS4gVGhlaXIgYWdlIGFuZCBnZW5kZXIgCjIuIFRoZWlyIGxldmVsIG9mIE5ldXJvdGljaXNtLCBhY2NvcmRpbmcgdG8gdGhlIE5ldXJvdGljaXNtIHN1YnNjYWxlIG9mIHRoZSBCaWcgRml2ZSBJbnZlbnRvcnkgKEJGSSkgCjMuIFNhdGlzZmFjdGlvbiB3aXRoIExpZmUgU2NhbGUgKFNXTFMpLiAKCgpXZSBjYW4gY3JlYXRlIGNvbXBvc2l0ZSB2YXJpYWJsZXMgZm9yIHRoZXNlIDIgc2NhbGVzIGFuZCBhZGQgdGhlbSB0byB0aGUgbWFpbiBkYXRhc2V0LgoKYGBge3IgY3JlYXRlIGRhdGEgZnJhbWUgd2l0aCBvbmx5IGludGFrZSBxdWVzdGlvbm5haXJlIHJvd3N9CiAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBvbmx5IGludGFrZSBxdWVzdGlvbm5haXJlIHJvd3MKaW50YWtlX2RhdGEgPC0gZmlsdGVyKGRhdGEsIHF1ZXN0aW9ubmFpcmUgPT0gIkNvbnNlbnQgYW5kIGludGFrZSBxdWVzdGlvbm5haXJlIikKCiMgY2hlY2sgdGhhdCBldmVyeW9uZSB0aGF0IGlzIGluIGRhdGEgaXMgYWxzbyBpbiBpbnRha2VfZGF0YSAtPiBldmVyeW9uZSBjb21wbGV0ZWQgaW50YWtlIHN1cnZleQpzZXRlcXVhbChkYXRhJHBhcnRpY2lwYW50LCBpbnRha2VfZGF0YSRwYXJ0aWNpcGFudCkKYGBgCgpOb3cgdGhhdCB3ZSBoYXZlIGEgbmV3IGRhdGEgZnJhbWUgd2l0aCBvbmx5IHRoZSBpbnRha2UgcXVlc3Rpb25uYWlyZSByb3dzLCB3ZSBjcmVhdGUgdGhlICoqY29tcG9zaXRlIHZhcmlhYmxlIGZvciB0aGUgU1dMUyoqLCB3aGljaCBpcyB0aGUgc3VtIG9mIGl0cyA1IGl0ZW1zOgoKYGBge3IgY29tcG9zaXRlIHZhcmlhYmxlIGZvciBTV0xTfQojIGNyZWF0ZSBjb21wb3NpdGUgdmFyaWFibGVzCiMgRm9yIGxpZmUgU2F0aXNmYWN0aW9uOiBzdW0gb2YgdGhlIDUgaXRlbXMKaW50YWtlX2RhdGEgPC0gaW50YWtlX2RhdGEgfD4gCiAgbXV0YXRlKGxpZmVfc2F0aXNmYWN0aW9uID0gcm93U3VtcyhwaWNrKCAKICAgICJTV0xTXzFfbGlrZXJ0IiwKICAgICJTV0xTXzJfbGlrZXJ0IiwKICAgICJTV0xTXzNfbGlrZXJ0IiwKICAgICJTV0xTXzRfbGlrZXJ0IiwKICAgICJTV0xTXzVfbGlrZXJ0IgogICkpKQoKIyBBbHRlcm5hdGl2ZWx5LCBpZiB5b3UgaGF2ZSBtYW55IGNvbHVtbnMgeW91IG5lZWQgdG8gc3VtLCB5b3UgY2FuIGJlc3QgZG8gc28gdXNpbmcgYQojIHJlZ3VsYXIgZXhwcmVzc2lvbjoKaW50YWtlX2RhdGEgPC0gaW50YWtlX2RhdGEgfD4gCiAgbXV0YXRlKGxpZmVfc2F0aXNmYWN0aW9uID0gcm93U3VtcyhwaWNrKG1hdGNoZXMoIlNXTFNfWzEtNV1fbGlrZXJ0IikpKSkKYGBgCgpGb3IgdGhlICoqY29tcG9zaXRlIHZhcmlhYmxlIGZvciBuZXVyb3RpY2lzbToqKiwgd2UgdGFrZSB0aGUgbWVhbiBvZiB0aGUgOCBpdGVtcy4gSG93ZXZlciwgdGhlIHZhcmlhYmxlcyBuYW1lZCB3aXRoIGFuICJSIiBhZnRlciB0aGUgcXVlc3Rpb24gbnVtYmVyIChsaWtlIGAiQkZJX25ldXJvdGljaXNtXzlSX211bHRpcGxlQ2hvaWNlX2xpa2VydCJgKSBuZWVkIHRvIGJlIHJldmVyc2VkLgpgYGB7ciBjb21wb3NpdGUgdmFyaWFibGUgZm9yIEJGSX0KIyBGb3IgZWFjaCBjb2x1bW4gdGhhdCBtYXRjaGVzIG91ciByZWd1bGFyIGV4cHJlc3Npb24sIFdlIGFwcGx5IHRoZSBmdW5jdGlvbiAoYFwoeClgIGlzIGEKIyBzaG9ydGhhbmQgZm9yIGBmdW5jdGlvbih4KWApIGA2IC0geGAsIHRvIHJldmVyc2UgdGhlIHNjb3JlcyBvZiB0aGUgaXRlbXMuIFRoZSByZWd1bGFyCiMgZXhwcmVzc2lvbiBpcyBhcyBmb2xsb3dzOiBUaGUgbGl0ZXJhbCBzdHJpbmcgIkJGSV9uZXVyb3RpY2lzbV9gIGZvbGxvd2VkIGJ5IGF0IGxlYXN0IG9uZQojICh0aGF0InMgd2hlcmUgdGhlIGArYCBzaWduIGlzIGZvcikgbnVtYmVyICh0aGUgYFxcZGAgY2hhcmFjdGVyKSBmb2xsb3dlZCBieSB0aGUgbGV0dGVyIFIKIyBhbmQgdGhlbiB0aGUgbGl0ZXJhbCBzdHJpbmcgIl9saWtlcnQiLiBUaGlzIHNob3VsZCBtYXRjaCBhbGwgb2YgdGhlIHJldmVyc2Uta2V5ZWQgQkZJCiMgaXRlbXMuIE9wdGlvbmFsbHksIHdlIGNhbiByZWVuY29kZSB0aGUgbmFtZXMgdXNpbmcgYSBgZ2x1ZTo6Z2x1ZSgpYCBleHByZXNzaW9uLCB3aGVyZSB3ZQojIGNhbiBpbnNlcnQgY29kZSB0byBydW4gdGhlIGZ1bmN0aW9uIGBnc3ViKClgIHRvIHJlcGxhY2UgdGhlIGxldHRlciAiUiIgd2l0aCBub3RoaW5nLCB0bwojIG1hdGNoIHRoZSBuYW1lcyBvZiB0aGUgb3RoZXIgQkZJIGl0ZW1zLgppbnRha2VfZGF0YSA8LSBpbnRha2VfZGF0YSB8PiAKICAgIG11dGF0ZShhY3Jvc3MoCiAgICAgICAgLmNvbHMgPSBtYXRjaGVzKCJCRklfbmV1cm90aWNpc21fXFxkK1JfbGlrZXJ0IiksCiAgICAgICAgLmZucyA9IFwoeCkgNiAtIHgsCiAgICAgICAgLm5hbWVzID0gIntnc3ViKCdSJywgJycsIC5jb2wpfSIKICAgICkpIAoKIyBXZSBjYW4gdGhlbiBjYWxjdWxhdGUgdGhlIG1lYW4gb2YgdGhlIEJGSSBpdGVtcyB1c2luZyBhIHJlZ3VsYXIgZXhwcmVzc2lvbiBhcyBiZWZvcmUuCiMgVGhlIGZ1bmN0aW9uIGBtYXRjaGVzKClgIHBpY2tzIGFsbCB0aGUgY29sdW1ucyBjb3JyZXNwb25kaW5nIHRvIHRoZSBCRkksIGJ1dCBvbmx5IHRoZSAKIyByZXZlcnNlZCBvbmVzIChpdCBkb2VzIG5vdCBwaWNrIGNvbHVtbnMgd2l0aCBhbiBSIGxpa2UgQkZJX25ldXJvdGljaXNtXzlSX2xpa2VydCkKaW50YWtlX2RhdGEgPC0gaW50YWtlX2RhdGEgfD4gCiAgbXV0YXRlKG5ldXJvdGljaXNtID0gcm93TWVhbnMocGljayggCiAgICBtYXRjaGVzKCJCRklfbmV1cm90aWNpc21fWzQsOSwxNCwxOSwyNCwyOSwzNCwzOV1fbGlrZXJ0IikKICApKSkKCmBgYAoKTm93IHRoYXQgd2UgY3JlYXRlZCB0aGUgY29tcG9zaXRlIHZhcmlhYmxlcyBmb3IgdGhlIGludGFrZSBxdWVzdGlvbm5haXJlLCB3ZSBjYW4gam9pbiB0aGVtIHRvIHRoZSBtYWluIGRhdGFzZXQuIFRoaXMgYWxzbyBtZWFucyB0aGF0IHdlIGNhbiByZW1vdmUgdGhlIHJvd3MgY29ycmVzcG9uZGluZyB0byB0aGUgaW50YWtlIHF1ZXN0aW9ubmFpcmUgZnJvbSB0aGUgbWFpbiBkYXRhc2V0LCBhcyB3ZSBoYXZlIGFscmVhZHkgZXh0cmFjdGVkIHRoZSBpbmZvcm1hdGlvbiB3ZSBuZWVkZWQuCgpgYGB7cn0KIyBTZWxlY3Qgb25seSByZWxldmFudCBjb2x1bW5zIGZvciBgaW50YWtlX2RhdGFgCmludGFrZV9kYXRhIDwtIGludGFrZV9kYXRhIHw+IAogIHNlbGVjdCgKICAgICJwYXJ0aWNpcGFudCIsICJnZW5kZXIiLCAiZ2VuZGVyX3N0cmluZyIsICJhZ2UiLCAKICAgICJsaWZlX3NhdGlzZmFjdGlvbiIsICJuZXVyb3RpY2lzbSIKICApCgojIEZpcnN0LCByZW1vdmUgdGhlIHJvd3MgY29ycmVzcG9uZGluZyB0byB0aGUgaW50YWtlIHF1ZXN0aW9ubmFpcmUgZnJvbSB0aGUgbWFpbiBkYXRhc2V0CiMgYXMgd2VsbCBhcyB0aGUgY29sdW1uIGZyb20gdGhlIGludGFrZSBxdWVzdGlvbm5haXJlCmRhdGEgPC0gZGF0YSB8PiAKICBmaWx0ZXIocXVlc3Rpb25uYWlyZSAhPSAiQ29uc2VudCBhbmQgaW50YWtlIHF1ZXN0aW9ubmFpcmUiKSB8PiAKICBzZWxlY3QoLSJjb25zZW50X3llc25vIjotIkJGSV9uZXVyb3RpY2lzbV8zOV9saWtlcnQiKSAKCiMgVGhlbiwgbWF0Y2ggdGhlIGludGFrZSBkYXRhIHdpdGggdGhlIGNvbXBvc2l0ZSBzY29yZXMgYmFjayB0byB0aGUgbWFpbiBkYXRhc2V0IGJ5IHVzaW5nCiMgdGhlIHBhcnRpY2lwYW50IG51bWJlciBhcyBhIGtleQojIEFsc28gcmVsb2NhdGUgdGhlIGNvbHVtbnMgZnJvbSB0aGUgaW50YWtlIGRhdGEgdG8gYmUgcGxhY2VkIGJlZm9yZSB0aGUgRVNNIGNvbHVtbnMgKGJ1dAojIGFmdGVyIHRoZSAiZGVmYXVsdCIgY29sdW1ucykKZGF0YSA8LSBkYXRhIHw+IAogIGxlZnRfam9pbihpbnRha2VfZGF0YSwgYnkgPSAicGFydGljaXBhbnQiKSB8PiAKICByZWxvY2F0ZSgiZ2VuZGVyIjoibmV1cm90aWNpc20iLCAuYmVmb3JlID0gInNsaWRlcl9oYXBweSIpCmBgYAoKIyMgQ2hlY2tpbmcgZm9yIGR1cGxpY2F0ZWQgdmFyaWFibGVzCkZvbGxvd2luZyB0aGUgW0VTTSBQcmVwcm9jZXNzaW5nIEdhbGxlcnkgZ3VpZGVsaW5lcyBmb3IgZHVwbGljYXRpb25dKGh0dHBzOi8vcHJlcHJvY2Vzcy5lc210b29scy5jb20vcGFnZXMvNDBfRHVwbGljYXRpb24uaHRtbCksIHdlIHdpbGwgY2hlY2sgZm9yIHRoZSBmb2xsb3dpbmcgdGhyZWUgdHlwZXMgb2YgZHVwbGljYXRpb246CgoqKjEuIER1cGxpY2F0ZWQgcm93czoqKiB0d28gcm93cyB0aGF0IGFyZSBleGFjdGx5IGlkZW50aWNhbC4gV2UgZG8gbm90IGhhdmUgYW55OgpgYGB7ciBjaGVjayBmb3IgZHVwbGljYXRlZCByb3dzfQojIFJldHVybnMgMCBpZiB0aGVyZSBhcmUgbm8gZHVwbGljYXRpb25zLCBvciB0aGUgaW5kaWNlcyBvZiB0aGUgZHVwbGljYXRlZCByb3dzIG90aGVyd2lzZQphbnlEdXBsaWNhdGVkKGRhdGEpIApgYGAKCioqMi4gRHVwbGljYXRlZCBhbnN3ZXJzOioqIHdlIHdhbnQgdG8gY2hlY2sgd2hldGhlciBhbnkgb2JzZXJ2YXRpb24gaGF2ZSBleGFjdGx5IHRoZSBzYW1lIGFuc3dlcnMuIFdlIGRvIG5vdCBoYXZlIGFueToKYGBge3IgY2hlY2sgZm9yIGR1cGxpY2F0ZWQgYW5zd2Vycywgd2FybmluZyA9IEZBTFNFfQojIExvYWQgdGhlIHBhY2thZ2UgYHRpZHlyYCB0byB1c2UgYGRyb3BfbmEoKWAgdG8gcmVtb3ZlIG1pc3NpbmcgdmFsdWVzCmxpYnJhcnkodGlkeXIpCgpkdXBsaWNhdGVkX2Fuc3dlcnMgPC0gZGF0YSB8PiAKICBkcm9wX25hKHN0YXJ0KSB8PiAgIyByZW1vdmUgcm93cyB0aGF0IGNvcnJlc3BvbmQgdG8gdW5hbnN3ZXJlZCBiZWVwcwogIGdyb3VwX2J5KHBpY2soInNsaWRlcl9oYXBweSI6bGFzdF9jb2woKSkpIHw+ICMgR3JvdXAgYnkgYWxsIHRoZSBpdGVtcwogIGZpbHRlcihuKCkgPiAxKSAjIGNoZWNrIGZvciBkdXBsaWNhdGVkIGFuc3dlcnMgaW4gdGhhdCBjb2x1bW4KCmR1cGxpY2F0ZWRfYW5zd2VycwpgYGAKCioqMy4gRHVwbGljYXRlZCB0aW1lIHN0YW1wcyoqIHdlIHdpbGwgY2hlY2sgdGhhdCB0aGUgc2FtZSBwYXJ0aWNpcGFudCBkb2VzIG5vdCBoYXZlIGR1cGxpY2F0ZWQgdGltZSBzdGFtcHMuCgpGaXJzdCwgd2Ugd2lsbCBjaGVjayBmb3IgZHVwbGljYXRlcyBpbiB0aGUgYHNlbnRgIHZhcmlhYmxlLgoKYGBge3IgY2hlY2sgZm9yIGR1cGxpY2F0ZWQgdGltZSBzdGFtcHN9CiMgRGlmZmVyZW50IHBhcnRpY2lwYW50cyBjYW4gY29ycmVjdGx5IGhhdmUgdGhlIHNhbWUgdGltZSBzdGFtcAojIHNvIHdlIHdpbGwganVzdCBjaGVjayB0aGF0IHRoZSBzYW1lIHBhcnRpY2lwYW50IGRvZXMgbm90IGhhdmUgZHVwbGljYXRlZCB0aW1lIHN0YW1wczoKZHVwbGljYXRlZF9zZW50IDwtIGRhdGEgfD4gCiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIHNlbnQpIHw+IAogIGZpbHRlcihuKCkgPiAxKSAKCmR1cGxpY2F0ZWRfc2VudApgYGAKSXQgc2VlbXMgdGhhdCBvbmUgcGFydGljaXBhbnQgYW5zd2VyZWQgdGhlIGV2ZW5pbmcgcXVlc3Rpb25uYWlyZSB0d2ljZS4gSW4gb3VyIGNhc2UsIHRoaXMgd2FzIHBvc3NpYmxlIGJlY2F1c2UgcGFydGljaXBhbnRzIHdlcmUgYWxsb3dlZCB0byBmaWxsIHRoZSBldmVuaW5nIHF1ZXN0aW9ubmFpcmUgbXVsdGlwbGUgdGltZXMgaWYgdGhleSBjbGlja2VkIG9uIHRoZSAicmVwZWF0IHF1ZXN0aW9ubmFpcmUiIGJ1dHRvbiBpbiB0aGUgYXBwLgoKV2UgY2FuIGp1c3QgcmVtb3ZlIHRoZSBxdWVzdGlvbm5haXJlIHRoYXQgd2FzIGFuc3dlcmVkIGxhdGVyLCBhcyBmb2xsb3dzOgpgYGB7ciByZW1vdmUgZHVwbGljYXRlZCB0aW1lIHN0YW1wfQojIEZvciBlYWNoIHBhcnRpY2lwYW50IGFuZCBzZW50IHRpbWUsIHRha2UgdGhlIGVhcmxpZXN0IGBzdGFydGAgdGltZSAoYW5kIHRoZXJlYnkgcmVtb3ZpbmcKIyBsYXRlciBgc3RhcnRgIHRpbWVzKS4gVGhpcyB3aWxsIHJlbW92ZSB0aGUgZHVwbGljYXRlZCB0aW1lIHN0YW1wLgpkYXRhIDwtIGRhdGEgfD4gCiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIHNlbnQpIHw+CiAgc2xpY2VfbWluKHN0YXJ0LCBuID0gMSwgd2l0aF90aWVzID0gRkFMU0UpIHw+IAogIHVuZ3JvdXAoKQpgYGAKCk5vdywgd2UgY2FuIGNoZWNrIHRoYXQgdGhlcmUgYXJlIG5vIGR1cGxpY2F0ZXMgaW4gdGhlIGBzdGFydGAgY29sdW1uOgoKYGBge3IgY2hlY2sgZm9yIGR1cGxpY2F0ZXMgaW4gdGhlIHN0YXJ0IGNvbHVtbn0KZHVwbGljYXRlZF9zdGFydCA8LSBkYXRhIHw+IAogIGRyb3BfbmEoc3RhcnQpIHw+ICAjIHJlbW92ZSByb3dzIHRoYXQgY29ycmVzcG9uZCB0byB1bmFuc3dlcmVkIGJlZXBzCiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIHN0YXJ0KSB8PiAKICBmaWx0ZXIobigpID4gMSkKCm5yb3coZHVwbGljYXRlZF9zdGFydCkgIyBubyBkdXBsaWNhdGVkIHN0YXJ0IHRpbWUgc3RhbXBzIQpgYGAKVGhlcmUgYXJlIG5vIGR1cGxpY2F0ZXMgaW4gYHN0YXJ0YC4KCiMjIEZpcnN0IG1pc3NpbmduZXNzIGFuYWx5c2lzCgpJdCJzIGltcG9ydGFudCB0byBpbnZlc3RpZ2F0ZSBhbmQgdW5kZXJzdGFuZCBtaXNzaW5nIHZhbHVlcyBhcyBpdCBjYW4gaGVscCB1cyB1bmRlcnN0YW5kIHBhcnRpY2lwYW50cyByZXNwb25zZSBwYXR0ZXJucywgYW5kIGlkZW50aWZ5IGRhdGEgY29sbGVjdGlvbiBlcnJvcnMuIAoKSW4gb3VyIGNhc2UsIHBhcnRpY2lwYW50cyB3ZXJlIG5vdCBhYmxlIHRvIHNraXAgcXVlc3Rpb25zLCBzbyBtaXNzaW5nIHZhbHVlcyBtYXkgYWxzbyBpbmRpY2F0ZSBlcnJvcnMgaW4gdGhlIGRhdGEgY29sbGVjdGlvbiBwcm9jZXNzLiBIb3dldmVyLCB3ZSBkaWQgaGF2ZSBicmFuY2hpbmcgcXVlc3Rpb25zIGluIHRoZSBldmVuaW5nIHF1ZXN0aW9ubmFpcmUsIHdoZXJlIHBhcnRpY2lwYW50cyB3ZXJlIGFza2VkIGlmIHRoZXkgaGFkIGhhZCBhIHN0cmVzc2Z1bC9wb3NpdGl2ZSBldmVudCB0aGF0IGRheSBhbmQgd2VyZSBwcm9tcHRlZCB0byBkZXNjcmliZSB0aGUgZXZlbnQgb25seSBpZiB0aGV5IGFuc3dlcmVkICJ5ZXMiLiBJZiB0aGV5IGFuc3dlcmVkICJubyIsIHRoZXkgd2VyZSBub3QgYXNrZWQsIHRoZXJlZm9yZSwgYE5BYCB2YWx1ZXMgaW4gdGhlc2UgY29sdW1ucyBhcmUgZXhwZWN0ZWQuIAoKTm90ZSB0aGF0IGV4cGxvcmluZyBgTkFgIHZhbHVlcyBpcyBkaWZmZXJlbnQgZnJvbSBleHBsb3JpbmcgcGFydGljaXBhbnQgY29tcGxpYW5jZSAodGhlaXIgcHJvcG9ydGlvbiBvZiBhbnN3ZXJlZCBiZWVwcykuIFdlIHdpbGwgZXhwbG9yZSBwYXJ0aWNpcGFudCBjb21wbGlhbmNlIGluIGEgbGF0ZXIgc3RlcC4gCgpGaXJzdCwgd2UgY2FuIHVzZSB0aGUgYHZpc19taXNzKClgIGZ1bmN0aW9uIGZyb20gdGhlIGB2aXNkYXRgIHBhY2thZ2UgdG8gdmlzdWFsaXplIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZGF0YXNldC4gVGhlIHggYXhpcyByZXByZXNlbnRzIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQgYW5kIHRoZSB5IGF4aXMgcmVwcmVzZW50cyB0aGUgcm93cy4gVGhlIGJsYWNrIGNlbGxzIHJlcHJlc2VudCBwcmVzZW50IHZhbHVlcywgYW5kIHRoZSB3aGl0ZSBjZWxscyByZXByZXNlbnQgbWlzc2luZyB2YWx1ZXMuIAoKV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSBzb21lIGNvbHVtbnMgd2l0aCBhIGxvdCBvZiBtaXNzaW5nIHZhbHVlcywgd2hpY2ggaXMgZXhwZWN0ZWQgc2luY2Ugd2UgYXJlIGRpc3BsYXlpbmcgZGF0YSBmcm9tIHR3byBkaWZmZXJlbnQgcXVlc3Rpb25uYWlyZXMuIFdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZXJlIGFyZSBzb21lIHBhcnRpY2lwYW50cyB0aGF0IGRpZCBub3Qgc2lnbiB1cCB3aXRoIGEgImNvZGUiLgoKYGBge3IgcGxvdCBtaXNzaW5nIHZhbHVlc30KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHZpc2RhdCkKCiMgVGhpcyBwbG90IG1ha2VzIG1vcmUgc2Vuc2UgaWYgdGhlIGRhdGEgaXMgb3JkZXJlZCBieSBgcGFydGljaXBhbnRgIGFuZCBgc3RhcnRgCmRhdGEgPC0gYXJyYW5nZShkYXRhLCBwYXJ0aWNpcGFudCwgc3RhcnQpCgp2aXNfbWlzcyhkYXRhKSArIAogIGdndGl0bGUoIk1pc3NpbmcgdmFsdWVzIikKYGBgCgpXZSBoYXZlIGFsc28gc2VlbiB0aGF0IHNvbWUgY29sdW1ucyBtYXkgaGF2ZSBhbGwgbWlzc2luZyB2YWx1ZXMuIEl0IHdvdWxkIGJlIGludGVyZXN0aW5nIHRvIGNoZWNrIHdoaWNoIGNvbHVtbnMgdGhleSBhcmUsIGFuZCByZW1vdmUgdGhlbSBpZiB0aGV5IGFyZSBub3QgdXNlZnVsIGZvciB0aGUgYW5hbHlzaXMuCgpgYGB7ciBjaGVjayB3aGljaCBjb2x1bW5zIGhhdmUgYWxsIE5BfX0KIyBDcmVhdGUgYSBoZWxwZXIgZnVuY3Rpb24gdGhhdCBjaGVja3Mgd2hldGhlciBhbGwgbWlzc2luZyB2YWx1ZXMgYXJlIG1pc3NpbmcKYWxsX25hIDwtIGZ1bmN0aW9uKGNvbCkgYWxsKGlzLm5hKGNvbCkpCgojIEdldCBjb2xzIHRoYXQgaGF2ZSBhbGwgTkEKbmFfY29scyA8LSBsYXBwbHkoZGF0YSwgYWxsX25hKSAKbmFfY29scyA8LSBhcy5sb2dpY2FsKG5hX2NvbHMpCgojIGNoZWNraW5nIHRoZSBuYW1lcyBvZiB0aGVzZSBjb2x1bW5zLiBXZSBoYXZlIDQgY29scyB3aXRoIGFsbCBOQQpuYW1lcyhkYXRhKVtuYV9jb2xzXSAKYGBgCgpUaGUgZm91ciBjb2x1bW5zIHdpdGggYWxsIGBOQWAgYXJlOiBgciBuYW1lcyhkYXRhKVtuYV9jb2xzXWAuIFRoZXNlIGNvcnJlc3BvbmQgdG8gbXVsdGlwbGUgY2hvaWNlIHF1ZXN0aW9ucyB3aGVyZSBhbiAiQW5zd2VyIHZhbHVlIiB3YXMgbm90IHByb3ZpZGVkIGR1cmluZyBxdWVzdGlvbm5haXJlIGNyZWF0aW9uLiBUaGVyZWZvcmUsIGl0IGlzIGNvbXBsZXRlbHkgZXhwZWN0ZWQgdGhhdCB0aGVzZSBjb2x1bW5zIGFyZSBlbXB0eS4gV2Ugd2lsbCByZW1vdmUgdGhlbSB3aXRoOiAgCgpgYGB7ciByZW1vdmUgY29sdW1ucyB3aXRoIGFsbCBOQX0KZGF0YSA8LSBkYXRhWywgIW5hX2NvbHNdICMgcmVtb3ZpbmcgY29scyB3aXRoIGFsbCBOQQoKIyBUaWR5dmVyc2UgYWx0ZXJuYXRpdmUKZGF0YSA8LSBzZWxlY3QoZGF0YSwgLXdoZXJlKGFsbF9uYSkpCmBgYAoKV2UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIG1pc3NlZCBiZWVwcyBoYXZlIGBOQWBzIGluIHRoZSBgc3RhcnRgIHZhcmlhYmxlLCBhbmQgdGhlIGFuc3dlcmVkIGJlZXBzIGRvIG5vdC4gV2UgY2FuIHVzZSB0aGlzIGluZm9ybWF0aW9uIHRvIGNyZWF0ZSBhbiBhZGRpdGlvbmFsIHZhcmlhYmxlIHRoYXQgaW5kaWNhdGVzIHdoZXRoZXIgdGhlIGJlZXAgd2FzIGFuc3dlcmVkIG9yIG5vdC4KCmBgYHtyIGNyZWF0ZSBhbnN3ZXJlZCB2YXJpYWJsZX0KZGF0YSA8LSBkYXRhIHw+IAogIG11dGF0ZShhbnN3ZXJlZCA9ICFpcy5uYShzdGFydCkpICMgbWlzc2VkIGJlZXBzIHdpbGwgYmUgRkFMU0UsIGFuc3dlcmVkIGJlZXBzIHdpbGwgYmUgVFJVRQpgYGAKCiMjIFJlZm9ybWF0IHRpbWUgc3RhbXBzCgpUaGUgdGltZSBzdGFtcHMgZnJvbSBtLVBhdGggYXJlIGluIFtVbml4IHRpbWVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1VuaXhfdGltZSkgYnV0IHdpdGggcmVzcGVjdCB0byB0aGUgbG9jYWwgdGltZSB6b25lIG9mIHRoZSBwYXJ0aWNpcGFudC4gVG8gd29yayB3aXRoIHRoZW0sIHdlIHdpbGwgcmVmb3JtYXQgdGhlbSB0byBhIG1vcmUgcmVhZGFibGUgZm9ybWF0LiBXZSB3aWxsIHVzZSB0aGUgYHRpbWVzdGFtcHNfdG9fZGF0ZXRpbWVgIGZ1bmN0aW9uIGZyb20gYG1wYXRocmAsIHRoYXQgd2lsbCBwdXQgdGhlIGRhdGVzIGluIHRoaXMgZm9ybWF0OiBgIiVZLSVtLSVkICVIOiVNOiVPUyJgLiBXZSBhcmUgc3BlY2lmeWluZyB0aGUgdGltZSB6b25lIGFzIGAiRXVyb3BlL0JydXNzZWxzImAgYmVjYXVzZSB3ZSBrbm93IG91ciBwYXJ0aWNpcGFudHMgd2VyZSBpbiB0aGlzIHRpbWUgem9uZS4gSG93ZXZlciwgaWYgeW91IGFyZSBub3QgbWVyZ2luZyB0aGUgZGF0YSB3aXRoIGFueSBvdGhlciBkYXRhc2V0LCB0aGUgdGltZXpvbmUgaXMgbm90IGNydWNpYWwgYW5kIGNhbiBiZSBsZWZ0IG91dC4KCmBgYHtyIHJlZm9ybWF0IHRpbWUgc3RhbXBzfQpkYXRhIDwtIGRhdGEgfD4gCiAgbXV0YXRlKGFjcm9zcygKICAgIC5jb2xzID0gYygic2NoZWR1bGVkIiwgInNlbnQiLCAic3RhcnQiLCAic3RvcCIpLAogICAgLmZucyA9IFwoeCkgdGltZXN0YW1wc190b19kYXRldGltZSh4LCBmb3JjZV90eiA9ICJFdXJvcGUvQnJ1c3NlbHMiKQogICkpCmBgYAoKTm93IHRoYXQgdGhlIGRhdGVzIGFyZSByZWZvcm1hdHRlZCwgd2UgY2FuIGNyZWF0ZSBzb21lIG90aGVyIGltcG9ydGFudCB0aW1lIHZhcmlhYmxlczogCgoxLiBXZSBjcmVhdGUgYSBjb2x1bW4gYG9ic19uYCB0aGF0IGZvciBlYWNoIHBhcnRpY2lwYW50LCBzaG93cyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB0aGUgb3JkZXIgdGhleSB3ZXJlIHNlbnQgKHN0YXJ0aW5nIGZyb20gMSB0byAxMTAsIHdoaWNoIGlzIHRoZSB0b3RhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHBlciBwYXJ0aWNpcGFudCkuIAoKMi4gVGhlIGNvbHVtbiBgZGF5X25gIHNob3dzIHRoZSBudW1iZXIgb2YgZGF5cyBzaW5jZSB0aGUgZmlyc3QgYmVlcCB3YXMgc2VudCBmb3IgZWFjaCBwYXJ0aWNpcGFudC4KCjMuIFRoZSBjb2x1bW4gYG9ic19uX2RheWAgc2hvd3MgdGhlIG9ic2VydmF0aW9uIG51bWJlciB3aXRoaW4gYSBkYXkgZm9yIGVhY2ggcGFydGljaXBhbnQgKHRoZSBmaXJzdCBiZWVwIG9mIHRoZSBkYXkgd2lsbCBiZSAxLCB1bnRpbCB0aGUgbGFzdCBiZWVwIG9mIHRoZSBkYXk6IDExKS4KCmBgYHtyIGNyZWF0ZSB0aW1lIHZhcmlhYmxlcywgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMgTGV0InMgZmlyc3QgY3JlYXRlIGEgaGVscGVyIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZGF5IG51bWJlciAoMSwgMiwgZXRjLikgc2luY2UgdGhlCiMgc3RhcnQgb2YgdGhlIHN0dWR5LiBCeSB1c2luZyBgZ3JvdXBfYnkoKWAgYmVmb3JlIGNhbGxpbmcgdGhpcyBmdW5jdGlvbiwgdGhlIGZ1bmN0aW9uCiMgaXMgY2FsbGVkIGVhY2ggdGltZSB3aXRoIGEgdmVjdG9yIG9mIGRhdGVzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHNhbWUgcGFydGljaXBhbnQuCmRheV9udW1iZXIgPC0gZnVuY3Rpb24odmVjKSB7CiAgIyBUcmFuc2Zvcm0gdGhlIHZlY3RvciB0byBkYXRlcyBOb3RlIHRoYXQgbHVicmlkYXRlOjpkYXRlKClgIHRha2VzIGludG8gYWNjb3VudCB0aGUgdGltZQogICMgem9uZSwgd2hpbGUgYGFzLkRhdGUoKWAgZG9lcyBub3QuCiAgdmVjIDwtIGRhdGUodmVjKQogIAogICMgR2V0IHRoZSBmaXJzdCBkYXkgaW4gdGhlIHZlY3RvcgogIGRheV9vbmUgPC0gbWluKHZlYywgbmEucm0gPSBUUlVFKQogIAogICMgQ2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGluIGRheXMsIGFuZCBhZGQgMSB0byBzdGFydCBmcm9tIDEKICBkaWZmcyA8LSBkaWZmdGltZSh2ZWMsIGRheV9vbmUsIHVuaXRzID0gImRheXMiKSArIDEKICAKICAjIENvbnZlcnQgdGhlIGRpZmZ0aW1lIG9iamVjdHMgdG8gaW50ZWdlcnMKICBhcy5pbnRlZ2VyKGRpZmZzKQp9CgojIEZpcnN0IHNvcnQgdGhlIGRhdGEgYnkgcGFydGljaXBhbnQgYW5kIHNlbnQgdGltZSBiZWNhdXNlIG9yZGVyaW5nIG1hdHRlcnMKZGF0YSA8LSBhcnJhbmdlKGRhdGEsIHBhcnRpY2lwYW50LCBzZW50KQoKIyBGb3IgZWFjaCBwYXJ0aWNpcGFudCwgY2FsY3VsYXRlIHRoZSB0b3RhbCBvYnNlcnZhdGlvbiBudW1iZXIuIFNpbmNlIGVhY2ggYmVlcCBpcyBhIHJvdywKIyB3ZSBjYW4gZG8gdGhpcyB1c2luZyBgcm93X251bWJlcigpYC4KIyBBbHNvIGZvciBlYWNoIHBhcnRpY2lwYW50LCBjYWxjdWxhdGUgdGhlIGRheSBudW1iZXIgc2luY2UgdGhlIHN0YXJ0IG9mIHRoZSBzdHVkeS4KZGF0YSA8LSBkYXRhIHw+IAogIGdyb3VwX2J5KHBhcnRpY2lwYW50KSB8PiAKICBtdXRhdGUob2JzX24gPSByb3dfbnVtYmVyKCkpIHw+IAogIG11dGF0ZShkYXlfbiA9IGRheV9udW1iZXIoc2VudCkpIHw+CiAgdW5ncm91cCgpCgojIFRoZW4gY2FsY3VsYXRlIHRoZSBvYnNlcnZhdGlvbiBudW1iZXIgd2l0aGluIGEgZGF5IGZvciBlYWNoIHBhcnRpY2lwYW50LCBhZ2FpbiB1c2luZwojIGByb3dfbnVtYmVyKClgLiAgCiMgQ3JlYXRlIG9ic2VydmF0aW9uIG51bWJlciB3aXRoaW4gZGF5CmRhdGEgPC0gZGF0YSB8PiAgIAogIGdyb3VwX2J5KHBhcnRpY2lwYW50LCBkYXlfbikgfD4gCiAgbXV0YXRlKG9ic19uX2RheSA9IHJvd19udW1iZXIoKSkgfD4gCiAgdW5ncm91cCgpCgpgYGAKCiMjIE1lcmdpbmcgd2l0aCBleHRlcm5hbCBkYXRhIHNvdXJjZXMKCkhlcmUsIHdlIHdpbGwgc2hvdyBob3cgdG8gbWVyZ2UgdGhlIGN1cnJlbnQgZGF0YXNldCB3aXRoIGFuIGV4dGVybmFsIGRhdGFzZXQuIAoKSW4gdGhlIGV4dGVybmFsIGRhdGFzZXQsIHdlIGhhdmUgZGF0YSB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBhdmVyYWdlIGhlYXJ0IHJhdGUgdGhyb3VnaG91dCB0aGUgZGF5IChub3RlOiB1bmxpa2UgdGhlIG90aGVyIGRhdGEsIHRoZSBleHRlcm5hbCBkYXRhIGRvZXMgbm90IGNvcnJlc3BvbmQgdG8gcmVhbCBkYXRhLCBpdCB3YXMgc2ltdWxhdGVkKS4gCgpgYGB7cn0KIyBMb2FkIHRoZSBleHRlcm5hbCBkYXRhc2V0CmV4dGVybmFsX2RhdGEgPC0gcmVhZC5jc3YoImV4dGVybmFsX2RhdGFzZXRfYnBtLmNzdiIpCgojIENoZWNrIHRoZSBkYXRhc2V0CmhlYWQoZXh0ZXJuYWxfZGF0YSkgCmBgYApUaGUgZXh0ZXJuYWwgZGF0YXNldCBoYXMgdGhlIGNvbHVtbnMgYGNvZGVgLCBgZGF5X25gIGFuZCBgYnBtX2RheWAsIGZvciBlYWNoIHBhcnRpY2lwYW50IGFuZCBmb3IgZWFjaCBkYXkgd2UgaGF2ZSB0aGUgbWVhbiBoZWFydCByYXRlLiAKClNpbmNlIHRoZSB2YWx1ZSB0byBsaW5rIHRoZSB0d28gZGF0YXNldHMgaXMgaW4gYGNvZGVgLCB3ZSBzaG91bGQgY2hlY2sgdGhhdCB0aGUgY29kZXMgYXJlIGluIG9yZGVyIChlLmcuIGFyZSB0aGVyZSBtaXNzaW5nIGNvZGVzPykuIFdlIGNhbiBjaGVjayB0aGF0IGVhY2ggY29kZSBjb3JyZXNwb25kcyB0byBvbmUgcGFydGljaXBhbnQgYnkgZXhhbWluaW5nIHRoZSB2YXJpYWJsZSJzIFtjb2hlcmVuY2VdKGh0dHBzOi8vcHJlcHJvY2Vzcy5lc210b29scy5jb20vcGFnZXMvNDBfQ2hlY2tfdmFyaWFibGVfY29oZXJlbmNlLmh0bWwpLiBXZSBjYW4gc2VlIGhlcmUgdGhhdCBvbmUgcGFydGljaXBhbnQgZG9lcyBub3QgaGF2ZSBhIGNvZGUsIHNvIHdlIHdpbGwgbm90IGJlIGFibGUgdG8gZ2V0IHRoZWlyIGhlYXJ0IHJhdGUgZGF0YS4gQnV0IG90aGVyd2lzZSBpdCBzZWVtcyBsaWtlIGVhY2ggY29kZSBjb3JyZXNwb25kcyB0byBvbmUgcGFydGljaXBhbnQ6CgpgYGB7cn0KZGlzdGluY3QoZGF0YSwgcGFydGljaXBhbnQsIGNvZGUpIApgYGAKCldlIGNhbiB1c2UgdGhlIGZvbGxvd2luZyBjb2RlIHRvIG1lcmdlIGl0IHdpdGggdGhlIG1haW4gZGF0YXNldDoKCmBgYHtyIG1lcmdlIHdpdGggZXh0ZXJuYWwgZGF0YX0KIyBXZSBtZXJnZSBieSBjb2RlIGFuZCBieSBkYXkgYmVjYXVzZSB3ZSBoYXZlIGEgaGVhcnQgcmF0ZSBtZWFuIGZvciBlYWNoIHBhcnRpY2lwYW50IGFuZAojIGVhY2ggZGF5CmRhdGEgPC0gbGVmdF9qb2luKAogIHggPSBkYXRhLAogIHkgPSBleHRlcm5hbF9kYXRhLCAKICBieSA9IGMoImNvZGUiLCAiZGF5X24iKQopIApgYGAKCiMjIEhhbmRsaW5nIG5lc3RlZCBkYXRhCgpXaGVuIHBhcnRpY2lwYW50cyBjYW4gcHJvdmlkZSBtdWx0aXBsZSBjaG9pY2VzIHRvIGEgcXVlc3Rpb24sIGByZWFkX21wYXRoKClgIHN0b3JlcyB0aGVzZSBhcyB2ZWN0b3JzIHN1Y2ggYXMgbGlzdCBjb2x1bW5zIG9mIG5lc3RlZCBkYXRhLCB3aGVyZSBlYWNoIHZhbHVlcyBpcyBhIHZlY3RvcmBjKDEsMiw0KWA6Cgp8IFBhcnRpY2lwYW50ICAgfCBkYXkgICAgICAgICAgIHwgY2hvaWNlIHwKfCAtLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLSB8LS0tLS0tLS0tLS0tLS0tLS0tIHwKfCAxICAgICAgICAgICAgIHwgMSAgICAgICAgICAgICB8IGMoMSwgMiwgNCkgICAgICAgIHwKfCAxICAgICAgICAgICAgIHwgMiAgICAgICAgICAgICB8IGMoMiwgMykgICAgICAgICAgIHwKfCAxICAgICAgICAgICAgIHwgMyAgICAgICAgICAgICB8IGMoNCkgICAgICAgICAgICAgIHwKCgpUbyBkZWFsIHdpdGggdGhlc2UgcmVzcG9uc2VzIGFuZCBhbmFseXplIHRoZW0sIHdlIGNhbiBjb252ZXJ0IHRoZSBkYXRhIHRvIGEgKipsb25nIGZvcm1hdCoqOiAod2hlcmUgZWFjaCBjaG9pY2UgaXMgYSBzZXBhcmF0ZSByb3cpLCBsaWtlIHRoaXM6IAoKfCBQYXJ0aWNpcGFudCAgIHwgZGF5ICAgICAgICAgICB8IGNob2ljZSB8CnwgLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0gfC0tLS0tLS0tLS0tLS0tLS0tLSB8CnwgMSAgICAgICAgICAgICB8IDEgICAgICAgICAgICAgfCAxICAgICAgICAgICAgICAgICB8CnwgMSAgICAgICAgICAgICB8IDEgICAgICAgICAgICAgfCAyICAgICAgICAgICAgICAgICB8CnwgMSAgICAgICAgICAgICB8IDEgICAgICAgICAgICAgfCA0ICAgICAgICAgICAgICAgICB8CgpXZSBjYW4gYWxzbyBjb252ZXJ0IHRoZSBkYXRhIHRvIGEgKip3aWRlIGZvcm1hdCoqICh3aGVyZSBlYWNoIGNob2ljZSBpcyBhIHNlcGFyYXRlIGNvbHVtbikuIAoKfCBQYXJ0aWNpcGFudCAgIHwgZGF5ICAgICAgICAgICB8IGNob2ljZV8xfCBjaG9pY2VfMnwgY2hvaWNlXzN8CnwgLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0gfC0tLSB8IC0tLSB8IC0tLSB8CnwgMSAgICAgICAgICAgICB8IDEgICAgICAgICAgICAgfCAxICB8IDIgICB8IDQgICB8CgpUaGUgZm9sbG93aW5nIGRhdGEgZXhlbXBsaWZpZXMgaG93IHRvIGNvbnZlcnQgdGhlIHRoZXNlIGtpbmQgb2YgY29sdW1ucyBib3RoIHRvIGEgbG9uZyBhbmQgdG8gYSB3aWRlIGZvcm1hdC4gVXNpbmcgb3VyIGNvbHVtbiBgZXZlbmluZ19hY3Rpdml0eV9pbmRleGAsIHdoaWNoIGNvbnRhaW5zIHZlY3RvcnMgb2Ygd2hpY2ggYWN0aXZpdGllcyBwZW9wbGUgaGFkIGRvbmUgZHVyaW5nIGEgc3BlY2lmaWMgZGF5OgoKYGBge3IgY3JlYXRlIGFjdGl2aXR5IGRhdGF9CiMgRGVsZXRlIHJvd3Mgd2l0aCBhbiBOQSBpbiB0aGlzIGNvbHVtbgphY3Rpdml0eV9kYXRhIDwtIGRhdGEgfD4gCiAgc2VsZWN0KCJwYXJ0aWNpcGFudCIsICJkYXlfbiIsICJldmVuaW5nX2FjdGl2aXR5X2luZGV4IikgfD4gCiAgZmlsdGVyKCFpcy5uYShldmVuaW5nX2FjdGl2aXR5X2luZGV4KSkKCmFjdGl2aXR5X2RhdGEKYGBgCgpOb3RlIHRoYXQgaW4gdGhlIGFib3ZlIGNvZGUsIHdlIGRlbGV0ZSByb3dzIHdpdGggYE5BYCBpbiB0aGUgY29sdW1uIGBldmVuaW5nX2FjdGl2aXR5X2luZGV4YC4gSW4gb3VyIGNhc2UsIHBhcnRpY2lwYW50cyB3ZXJlICoqbm90KiogYWxsb3dlZCB0byBsZWF2ZSB0aGlzIHF1ZXN0aW9uIGJsYW5rLCBzbyBgTkFgcyBpbmRpY2F0ZSB1bmFuc3dlcmVkIGJlZXBzLiBJbiBvdGhlciBjYXNlcywgYE5BYHMgbWF5IGluZGljYXRlIHRoYXQgdGhlIHBlcnNvbiByZXBvcnRlZCBkb2luZyBub25lIG9mIHRoZSBhY3Rpdml0aWVzIGxpc3RlZCwgc28gaXQgbWF5YmUgd291bGQgbm90IGJlIGEgZ29vZCBpZGVhIHRvIGRlbGV0ZSB0aGVzZSByb3dzLCBhcyB0aGV5IGFyZSBzdGlsbCBpbmZvcm1hdGl2ZS4KCiMjIyBMb25nIGZvcm1hdApgYGB7ciBjb252ZXJ0IHRvIGxvbmcgZm9ybWF0fQojIENvbnZlcnQgdG8gbG9uZyBmb3JtYXQgdXNpbmcgdW5uZXN0KCkKbG9uZ19kYXRhIDwtIGFjdGl2aXR5X2RhdGEgfD4gIAogIHVubmVzdChldmVuaW5nX2FjdGl2aXR5X2luZGV4KQoKbG9uZ19kYXRhCmBgYAoKIyMjIFdpZGUgZm9ybWF0CmBgYCB7ciBjb252ZXJ0IHRvIHdpZGUgZm9ybWF0fQp3aWRlX2RhdGEgPC0gYWN0aXZpdHlfZGF0YSB8PiAKICB1bm5lc3Rfd2lkZXIoZXZlbmluZ19hY3Rpdml0eV9pbmRleCwgbmFtZXNfc2VwID0gIl8iKQoKd2lkZV9kYXRhCmBgYAoKQW5vdGhlciBvcHRpb24gZm9yIGEgd2lkZSBkYXRhIGZyYW1lIGlzIHRvIGhhdmUgYmluYXJ5IGNvbHVtbnMgY29ycmVzcG9uZGluZyB0byBhbiBjaG9pY2UuIExpa2UgdGhpczogCgp8IFBhcnRpY2lwYW50ICAgfCBkYXkgICAgICAgICAgIHwgY2hvaWNlXzF8IGNob2ljZV8yfCBjaG9pY2VfM3wKfCAtLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLSB8LS0tIHwgLS0tIHwgLS0tIHwKfCAxICAgICAgICAgICAgIHwgMSAgICAgICAgICAgICB8IDAgIHwgMSAgIHwgMCAgIHwKfCAyICAgICAgICAgICAgIHwgMSAgICAgICAgICAgICB8IDEgIHwgMSAgIHwgMSAgIHwKCkluIHRoaXMgdGFibGUsIHBhcnRpY2lwYW50IG9uZSBjaG9zZSBjaG9pY2UgMiwgYnV0IGRpZG4idCBjaG9vc2UgY2hvaWNlIDEgb3IgMywgd2hpbGUgcGFydGljaXBhbnQgMiBjaG9zZSBhbGwgdGhyZWUgb3B0aW9ucy4gVGhpcyBjYW4gYmUgZG9uZSB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZToKCmBgYHtyIGNvbnZlcnQgdG8gd2lkZSBmb3JtYXQgKHdpdGggYmluYXJ5IHZhcmlhYmxlcyl9CmRhdGFfd2lkZV9iaW5hcnkgPC0gYWN0aXZpdHlfZGF0YSB8PiAKICB1bm5lc3QoZXZlbmluZ19hY3Rpdml0eV9pbmRleCkgCgpkYXRhX3dpZGVfYmluYXJ5IDwtIGRhdGFfd2lkZV9iaW5hcnkgfD4gCiAgbXV0YXRlKGFjdGl2aXR5ID0gcGFzdGUwKCJhY3Rpdml0eV8iLCBldmVuaW5nX2FjdGl2aXR5X2luZGV4KSkgfD4gCiAgcGl2b3Rfd2lkZXIoCiAgICBuYW1lc19mcm9tID0gImFjdGl2aXR5IiwKICAgIHZhbHVlc19mcm9tID0gImV2ZW5pbmdfYWN0aXZpdHlfaW5kZXgiLAogICAgdmFsdWVzX2ZpbGwgPSBsaXN0KGV2ZW5pbmdfYWN0aXZpdHlfaW5kZXggPSAwKSwKICAgIHZhbHVlc19mbiA9IGxpc3QoZXZlbmluZ19hY3Rpdml0eV9pbmRleCA9IGxlbmd0aCkKICApCgpkYXRhX3dpZGVfYmluYXJ5CmBgYAoKCiMjIFJlLW9yZGVyaW5nIGNvbHVtbnMKCkFzIGEgZmluYWwgc3RlcCwgd2Ugd2lsbCByZS1vcmRlciB0aGUgY29sdW1ucyB1c2luZyB0aGUgYHJlbG9jYXRlYCBmdW5jdGlvbiBmcm9tIGBkcGx5cmA6CgpgYGB7ciByZS1vcmRlciBjb2x1bW5zfQpkYXRhIDwtIGRhdGEgfD4gCiAgcmVsb2NhdGUoIm9ic19uIiwgImRheV9uIiwgIm9ic19uX2RheSIsICJhbnN3ZXJlZCIsICJicG1fZGF5IiwgLmJlZm9yZSA9ICJnZW5kZXIiKQpgYGAKCiMgU3RlcCAyIDogRGVzaWduIGFuZCBzYW1wbGluZyBzY2hlbWUKCkluIHRoaXMgW3N0ZXBdKGh0dHBzOi8vcHJlcHJvY2Vzcy5lc210b29scy5jb20vZnJhbWV3b3JrLmh0bWwjc3RlcC0yLWRlc2lnbi1hbmQtc2FtcGxpbmctc2NoZW1lKSwgd2UgZXhwbG9yZSB0aGUgZGVzaWduIGFuZCBzYW1wbGluZyBzY2hlbWUgb2YgdGhlIHN0dWR5LiBUaGUgbWFpbiBnb2FsIGlzIHRvIGV4YW1pbmUgdG8gd2hhdCBleHRlbnQgdGhlIGRhdGEgY29sbGVjdGlvbiBwcm9jZXNzIHdlbnQgYXMgZXhwZWN0ZWQsIGFuZCB0byBpZGVudGlmeSBwb3RlbnRpYWwgaXNzdWVzIHRoYXQgY291bGQgbW90aXZhdGUgZGF0YSBleGNsdXNpb24uIFdlIG1haW5seSB3aWxsIGNoZWNrIHRoZSBjb2hlcmVuY2Ugb2YgdGhlIHRpbWUgc3RhbXBzLCB0aGUgc2VudCBiZWVwcyBhbmQgdGhlIGR1cmF0aW9uIG9mIHRoZSBzdHVkeS4KCiMjIENvaGVyZW5jZSB0aW1lIHN0YW1wcyBhbmQgb2JzZXJ2YXRpb25zIG9yZGVyCgpJbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zLCB3ZSBwZXJmb3JtIHNvbWUgbG9naWNhbCB0ZXN0cyB0byBlbnN1cmUgdGhlIGNvaGVyZW5jZSBvZiB0aGUgdGltZSBzdGFtcHMgYW5kIHRoZSBvYnNlcnZhdGlvbnMuIEZvciBleGFtcGxlOiB3ZSBleHBlY3QgdGhhdCBmb3Igb25lIG9ic2VydmF0aW9uLCB0aGUgc2NoZWR1bGVkIHRpbWUgc2hvdWxkIGFsd2F5cyBiZSBiZWZvcmUgdGhlIHNlbnQgdGltZS4gSGF2aW5nIGJlZXBzIHdoZXJlIHRoaXMgaXMgbm90IHRoZSBjYXNlIGNvdWxkIGluZGljYXRlIGFuIGVycm9yIGluIHRoZSBkYXRhIGNvbGxlY3Rpb24gcHJvY2Vzcy4gRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gdGltZSBzdGFtcCBjb2hlcmVuY2UgYW5kIGhvdyB0byBjaGVjayBpdCwgc2VlIHRoZSBbRVNNIFByZXByb2Nlc3NpbmcgR2FsbGVyeV0oaHR0cHM6Ly9wcmVwcm9jZXNzLmVzbXRvb2xzLmNvbS9wYWdlcy81MF9Db2hlcmVuY2VfdGltZXN0YW1wc19vYnMuaHRtbCkuCgojIyMgQ29oZXJlbmNlIHdpdGhpbiBvYnNlcnZhdGlvbnMKCldpdGhpbiBhIHNpbmdsZSBvYnNlcnZhdGlvbiwgd2UgZXhwZWN0IHRoYXQgdGhlIHNjaGVkdWxlZCB0aW1lIGlzIGJlZm9yZSB0aGUgc2VudCB0aW1lLCB0aGUgc2VudCB0aW1lIGlzIGJlZm9yZSB0aGUgc3RhcnQgdGltZSwgYW5kIHRoZSBzdGFydCB0aW1lIGlzIGJlZm9yZSB0aGUgc3RvcCB0aW1lLiBXZSB3aWxsIGNoZWNrIHRoaXMgZm9yIGFsbCBvYnNlcnZhdGlvbnMuIAoKYGBge3IgY29oZXJlbmNlIHdpdGhpbiBvYnNlcnZhdGlvbn0KIyBDaGVja2luZyB0aGF0IHNjaGVkdWxlZCA8IHNlbnQgPCBzdGFydCA8IHN0b3AKCiMgY2hlY2sgaWYgYW55IHNjaGVkdWxlZCB0aW1lIGlzIGFmdGVyIHRoZSBzZW50IHRpbWUKYW55KGRhdGEkc2NoZWR1bGVkID4gZGF0YSRzZW50ICYgIWlzLm5hKGRhdGEkc2VudCkpIAoKIyBjaGVjayBpZiBhbnkgc2VudCB0aW1lIGlzIGFmdGVyIHRoZSBzdGFydCB0aW1lCmFueShkYXRhJHNlbnQgPiBkYXRhJHN0YXJ0ICYgIWlzLm5hKGRhdGEkc3RhcnQpKSAKCiMgY2hlY2sgaWYgYW55IHN0YXJ0IHRpbWUgaXMgYWZ0ZXIgdGhlIHN0b3AgdGltZQphbnkoZGF0YSRzdGFydCA+IGRhdGEkc3RvcCAmICFpcy5uYShkYXRhJHN0b3ApKSAKYGBgCkl0IGFsbCByZXR1cm5zIGBGQUxTRWAsIHdoaWNoIG1lYW5zIHRoYXQgYWxsIG9ic2VydmF0aW9ucyBhcmUgaW4gdGhlIGV4cGVjdGVkIG9yZGVyLgoKSWYgc29tZSBvYnNlcnZhdGlvbnMgd2VyZSBub3QgaW4gdGhlIGV4cGVjdGVkIG9yZGVyLCB0aGlzIGNvdWxkIGJlIGR1ZSB0byBwYXJ0aWNpcGFudHMgY2hhbmdpbmcgdGltZSB6b25lcywgb3IgY2hhbmdpbmcgZnJvbSBzdW1tZXIgdG8gd2ludGVyIHRpbWVzIChvciB0aGUgb3RoZXIgd2F5IGFyb3VuZCkuCgojIyMgQ29oZXJlbmNlIGJldHdlZW4gb2JzZXJ2YXRpb25zCgpXZSBzaG91bGQgYWxzbyB2ZXJpZnkgdGhhdCB0aGUgdGltZSBzdGFtcHMgYXJlIGluIHRoZSBjb3JyZWN0IG9yZGVyIGJldHdlZW4gb2JzZXJ2YXRpb25zLiBXZSBleHBlY3QgdGhhdCB0aGUgc2NoZWR1bGVkIHRpbWUgb2YgdGhlIGN1cnJlbnQgb2JzZXJ2YXRpb24gaXMgYmVmb3JlIHRoZSBzY2hlZHVsZWQgdGltZSBvZiB0aGUgbmV4dCBvYnNlcnZhdGlvbiwgYW5kIHRoZSBzYW1lIGZvciB0aGUgc2VudCwgc3RhcnQsIGFuZCBzdG9wIHRpbWVzLiAKCkhvd2V2ZXIsIGl0IG1heSBiZSB0aGF0IHBhcnRpY2lwYW50cyBhbnN3ZXJlZCB0aGUgNnRoIGJlZXAgYmVmb3JlIHRoZSA1dGggYmVlcCwgZHVlIHRvIGNsb3NlIGJlZXAgdGltZXMgYW5kIG92ZXJsYXBwaW5nIGV4cGlyYXRpb24gdGltZXMuIEluIHRoaXMgY2FzZSwgbWF5YmUgdGhlIG9yZGVyIG9mIHRoZSBvYnNlcnZhdGlvbnMgY291bGQgYmUgcmV2ZXJzZWQsIHNvIHRoYXQgb2JzZXJ2YXRpb24gNSBiZWNvbWVzIG9ic2VydmF0aW9uIDYuIAoKVG8gY2hlY2sgZm9yIGNvaGVyZW5jZSBiZXR3ZWVuIG9ic2VydmF0aW9ucywgd2Ugb3JkZXIgdGhlIGRhdGEgZnJhbWUgYWNjb3JkaW5nIHRvIG9uZSBvZiBvdXIgdGltZSB2YXJpYWJsZXMsIGFuZCBjaGVjayBmb3IgaXNzdWVzIGJldHdlZW4gY29uc2VjdXRpdmUgb2JzZXJ2YXRpb25zLgoKYGBge3IgY29oZXJlbmNlIGJldHdlZW4gb2JzZXJ2YXRpb25zfQojIGNoZWNrIGlmIHRoZSBzY2hlZHVsZWQgdGltZSBvZiB0aGUgY3VycmVudCBvYnNlcnZhdGlvbiBpcyBhZnRlciB0aGUgc2NoZWR1bGVkIHRpbWUgb2YKIyB0aGUgcHJldmlvdXMgb2JzZXJ2YXRpb24KIyBzaG93IG9ubHkgcm93cyB3aGVyZSBvbmUgb2YgdGhlc2UgaXNzdWVzIGlzIHByZXNlbnQKZGF0YSB8PiAKICBzZWxlY3QocGFydGljaXBhbnQsICJzY2hlZHVsZWQiLCAic2VudCIsICJzdGFydCIsICJzdG9wIikgfD4gCiAgYXJyYW5nZShwYXJ0aWNpcGFudCwgc2VudCkgfD4gCiAgZ3JvdXBfYnkocGFydGljaXBhbnQpIHw+IAogIG11dGF0ZSgKICAgIHNjaGVkX2xhZ19pc3N1ZSA9IGxhZyhzY2hlZHVsZWQpID4gc2NoZWR1bGVkLCAKICAgIHNlbnRfbGFnX2lzc3VlID0gbGFnKHNlbnQpID4gc2VudCwKICAgIHN0YXJ0X2xhZ19pc3N1ZSA9IGxhZyhzdGFydCkgPiBzdGFydCwKICAgIHN0b3BfbGFnX2lzc3VlID0gbGFnKHN0b3ApID4gc3RvcAogICkgfD4gCiAgZmlsdGVyKHNjaGVkX2xhZ19pc3N1ZSB8IHNlbnRfbGFnX2lzc3VlIHwgc3RhcnRfbGFnX2lzc3VlIHwgc3RvcF9sYWdfaXNzdWUpIApgYGAKCldlIGhhdmUgYSByb3cgdGhhdCBzaG93cyBhIGBzdGFydF9sYWdgIGlzc3VlLCB1cG9uIGZ1cnRoZXIgaW5zcGVjdGlvbiwgd2Ugc2VlIHRoYXQgdGhpcyBpcyBiZWNhdXNlIHRoZSBwYXJ0aWNpcGFudCBmaWxsZWQgYSBtYWluIHF1ZXN0aW9ubmFpcmUgYmVlcCBqdXN0IGFmdGVyIHRoZSBldmVuaW5nIHF1ZXN0aW9ubmFpcmUuIAoKVGhpcyBpcyBva2F5LCBiZWNhdXNlIGluIHRoZSBldmVuaW5nIHF1ZXN0aW9ubmFpcmUgcGFydGljaXBhbnRzIGFuc3dlciBhYm91dCB0aGVpciB3aG9sZSBkYXksIGFuZCBpbiB0aGUgIm1haW4gcXVlc3Rpb25uYWlyZSIgYmVlcCB0aGV5IGFuc3dlciBhYm91dCBob3cgdGhleSBmZWVsIGluIHRoYXQgbW9tZW50LiAKCiMjIER1cmF0aW9uIG9mIHRoZSBzdHVkeSBwZXIgcGFydGljaXBhbnQKCldlIHdpbGwgbm93IGNoZWNrIHRoZSBkdXJhdGlvbiBvZiB0aGUgc3R1ZHkgYnkgcGxvdHRpbmcgdGhlIGVhcmxpZXN0IGFuZCBsYXRlc3Qgc2VudCBkYXRlLiBXZSBleHBlY3QgdGhhdCB0aGUgZHVyYXRpb24gb2YgdGhlIHN0dWR5IGlzIHRoZSAxMCBkYXlzIGZvciBldmVyeW9uZS4KCmBgYHtyIHN0dWR5IGR1cmF0aW9uIHBlciBwYXJ0aWNpcGFudCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgTG9hZCB0aGUgYGdnYWx0YCBwYWNrYWdlIGZvciBgZ2VvbV9kdW1iYmVsbGAKbGlicmFyeShnZ2FsdCkKCiMgQ2FsY3VsYXRlIHRoZSBiZWdpbm5pbmcgYW5kIGVuZCB0aW1lIG9mIHRoZSBzdHVkeSBmb3IgZWFjaCBwYXJ0aWNpcGFudApzdHVkeV9kdXJhdGlvbiA8LSBkYXRhIHw+IAogIGdyb3VwX2J5KHBhcnRpY2lwYW50KSB8PiAKICBzdW1tYXJpc2UoCiAgICBmcm9tID0gbWluKHNlbnQsIG5hLnJtID0gVFJVRSksIAogICAgdG8gPSBtYXgoc2VudCwgbmEucm0gPSBUUlVFKQogICkgfD4gCiAgbXV0YXRlKHBhcnRpY2lwYW50ID0gYXMuZmFjdG9yKHBhcnRpY2lwYW50KSkgIyBGb3IgZ2dwbG90CgojIENyZWF0ZSBhIHBsb3Qgc2hvd2luZyB0aGUgc3R1ZHkgcnVuIHRpbWUgZm9yIGVhY2ggcGFydGljaXBhbnQKc3R1ZHlfZHVyYXRpb24gfD4gCiAgZ2dwbG90KGFlcygKICAgIHggPSBmcm9tLCAKICAgIHhlbmQgPSB0bywgCiAgICB5ID0gcGFydGljaXBhbnQKICApKSArIAogIGdlb21fZHVtYmJlbGwoY29sb3IgPSBkYXJrX21wYXRoX3BhbGV0dGVbMV0sIHNpemUgPSAwLjc1KSArCiAgc2NhbGVfeF9kYXRldGltZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJUIgJXkiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAiRHVyYXRpb24gb2YgdGhlIHN0dWR5IHBlciBwYXJ0aWNpcGFudCIsCiAgICB4ID0gIlRpbWUiLAogICAgeSA9ICJQYXJ0aWNpcGFudCIKICApCgojIFZlcmlmeSB0aGF0IHRoZXJlIGFyZSAxMCBkYXlzIGZvciBhbGwgcGFydGljaXBhbnRzCmRhdGEgfD4KICBncm91cF9ieShwYXJ0aWNpcGFudCkgfD4gCiAgc3VtbWFyaXNlKG1heF9kYXlzID0gbWF4KGRheV9uKSkKYGBgCgojIyBOdW1iZXIgb2YgYmVlcHMgKipzZW50KiogcGVyIHBhcnRpY2lwYW50CkFsbCBwYXJ0aWNpcGFudHMgaGF2ZSB0aGUgc2FtZSBudW1iZXIgb2YgYmVlcHMgc2VudCwgYXMgZXhwZWN0ZWQ6CgpgYGB7ciBudW1iZXIgb2Ygc2VudCBiZWVwc30Kbl9zZW50IDwtIGRhdGEgfD4gCiAgZHJvcF9uYShzZW50KSB8PiAKICBncm91cF9ieShwYXJ0aWNpcGFudCkgfD4gCiAgc3VtbWFyaXplKG4gPSBuKCkpIAoKIyBDcmVhdGUgYSBiYXIgY2hhcnQgd2l0aCB0aGUgbnVtYmVyIG9mIGJlZXBzIHNlbnQgcGVyIHBhcnRpY2lwYW50Cm5fc2VudCB8PiAKICBnZ3Bsb3QoYWVzKAogICAgeCA9IGZhY3RvcihwYXJ0aWNpcGFudCksICMgZmFjdG9yKCkgaXMgbmVlZGVkIHRvIG1ha2UgdGhlbSBpbnRvIGNhdGVnb3JpZXMKICAgIHkgPSBuCiAgKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIiwgZmlsbCA9IGRhcmtfbXBhdGhfcGFsZXR0ZVsyXSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJOdW1iZXIgb2YgYmVlcHMgc2VudCBwZXIgcGFydGljaXBhbnQiLAogICAgeCA9ICJQYXJ0aWNpcGFudCIsCiAgICB5ID0gIk51bWJlciBvZiBiZWVwcyIKICApCmBgYAoKIyMgRGVsYXkgYmV0d2VlbiB0d28gY29uc2VjdXRpdmUgYmVlcHMKTW9zdCBkZWxheXMgYXJlIGFyb3VuZCAxIGhvdXIgYmVjYXVzZSB0aGF0InMgaG93IHdlIHNwYWNlZCB0aGUgYmVlcHMgZHVyaW5nIHRoZSBkYXkuClRoZSBkZWxheXMgYXQgYXJvdW5kIDEwIGhvdXJzIGNvcnJlc3BvbmQgdG8gdGhlIGRlbGF5IGJldHdlZW4gdGhlIGxhc3QgYmVlcCBvZiB0aGUgZGF5IChldmVuaW5nIHF1ZXN0aW9ubmFpcmUpIGFuZCB0aGUgZmlyc3QgYmVlcCBvZiB0aGUgbmV4dCBkYXkuCgpgYGB7ciBkZWxheSBiZXR3ZWVuIHR3byBjb25zZWN1dGl2ZSBiZWVwcywgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmRmX2ludCA8LSBkYXRhIHw+CiAgYXJyYW5nZShwYXJ0aWNpcGFudCwgc2VudCkgfD4KICBncm91cF9ieShwYXJ0aWNpcGFudCkgfD4KICBtdXRhdGUoZGVsYXkgPSBkaWZmdGltZShsZWFkKHNlbnQpLCBzZW50LCB1bml0cyA9ICJob3VycyIpKSAKCmRmX2ludCB8PiAKICBnZ3Bsb3QoYWVzKHggPSBkZWxheSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwLCBmaWxsID0gZGFya19tcGF0aF9wYWxldHRlWzNdKSArIAogIGxhYnMoCiAgICB0aXRsZSA9ICJEZWxheSBiZXR3ZWVuIHR3byBjb25zZWN1dGl2ZSBiZWVwcyIsCiAgICB4ID0gIkRlbGF5IGJldHdlZW4gdHdvIGNvbnNlY3V0aXZlIGJlZXBzIChob3VycykiLAogICAgeSA9ICJDb3VudCIKICApCmBgYAoKIyMgQ29uY2x1c2lvbnM6IERlc2lnbiBhbmQgc2FtcGxpbmcgc2NoZW1lCgpJbiBnZW5lcmFsLCB3ZSBkaWQgbm90IGZpbmQgYW55IGlzc3VlcyB3aXRoIHRoZSBleHBlcmltZW50YWwgZGVzaWduOiAKCiogVGhlIHRpbWUgc3RhbXBzIGFyZSBjb2hlcmVudC4KKiBUaGUgbnVtYmVyIG9mIGJlZXBzIHNlbnQgaXMgZXF1YWwgZm9yIGFsbCBwYXJ0aWNpcGFudHMsIHdoaWNoIGluIG91ciBjYXNlIHdhcyBpbnRlbmRlZCAobm90ZSB0aGF0IGluIHNvbWUgZXhwZXJpbWVudHMsIHBhcnRpY2lwYW50cyBtYXkgcmVjZWl2ZSBkaWZmZXJlbnQgYmVlcHMgZGVwZW5kaW5nIG9uIHRoZWlyIGV4cGVyaW1lbnRhbCBncm91cCwgb3Igb3RoZXIgdmFyaWFibGVzLiBJbiB3aGljaCBjYXNlIGFuIGVxdWFsIG51bWJlciBvZiBiZWVwcyBwZXIgcGFydGljaXBhbnQgd291bGQgYmUgYSBwcm9ibGVtKS4KKiBUaGUgZGVsYXlzIGJldHdlZW4gYmVlcHMgYXJlIGFzIGV4cGVjdGVkLgoqIFRoZSBkdXJhdGlvbiBvZiB0aGUgc3R1ZHkgcGVyIHBhcnRpY2lwYW50IGlzIGFsc28gY29uc2lzdGVudCAod2UgZXhwZWN0ZWQgYW4gZXF1YWwgZHVyYXRpb24gb2YgMTAgZGF5cyBwZXIgcGFydGljaXBhbnQpLiAKCldlIGNhbiBub3cgbW92ZSBvbiB0byB0aGUgbmV4dCBzdGVwcyBvZiB0aGUgcHJlLXByb2Nlc3NpbmcuCgpOb3RlOiB3ZSBmb2xsb3dlZCBzdGVwcyB0aGF0IHdlcmUgcmVsZXZhbnQgZm9yIG91ciBzdHVkeSBkZXNpZ24sIGZvciBhIG1vcmUgY29tcHJlaGVuc2l2ZSBvdmVydmlldyBvZiBwb3NzaWJsZSBjaGVja3MgZm9yIHRoZSBzYW1wbGluZwphbmQgc3R1ZHkgZGVzaWduLCBzZWUgdGhlIFtFU00gUHJlcHJvY2Vzc2luZyBHYWxsZXJ5XShodHRwczovL3ByZXByb2Nlc3MuZXNtdG9vbHMuY29tLykuCgojIFN0ZXAgMzogUGFydGljaXBhbnRzIHJlc3BvbnNlIGJlaGF2aW9ycwoKSW4gdGhpcyBzdGVwLCB3ZSB3YW50IHRvIGxvb2sgbW9yZSBjbG9zZWx5IGF0IHBhcnRpY2lwYW50cyIgcmVzcG9uc2VzLiBXZSB3aWxsIGxvb2sgYXQgd2hldGhlciBwYXJ0aWNpcGFudHMgc2VlbWVkIHRvIGZvbGxvdyB0aGUgc2FtcGxpbmcgc2NoZW1lIGNvcnJlY3RseSwgYW5kIHdoZXRoZXIgdGhlcmUgYXJlIGFueSByZXNwb25zZSBwYXR0ZXJucyB0aGF0IGNvdWxkIHJlZHVjZSBkYXRhIHF1YWxpdHkuIFdlIHByaW1hcmlseSB3aWxsIGxvb2sgYXQgdGhlIHNhbXBsaW5nIHNjaGVtZSBwZXIgcGFydGljaXBhbnQsIHRoZSBkZWxheSB0byBmaWxsIGJlZXBzLCB0aGUgdGltZSBvZiBhbnN3ZXJlZCBhbmQgbWlzc2VkIGJlZXBzLCBhbmQgcGFydGljaXBhbnQgY29tcGxpYW5jZS4gCgpGb3IgYSBtb3JlIGNvbXByZWhlbnNpdmUgb3ZlcnZpZXcgb2YgcG9zc2libGUgY2hlY2tzIGZvciB0aGlzIHN0ZXAsIHNlZSB0aGUgW0VTTSBQcmVwcm9jZXNzaW5nIEdhbGxlcnldKGh0dHBzOi8vcHJlcHJvY2Vzcy5lc210b29scy5jb20vKS4KCiMjIFNhbXBsaW5nIHNjaGVtZSBwZXIgcGFydGljaXBhbnQKCiMjIyBCZWVwIGxldmVsCldpdGggdGhlIHBsb3QgYmVsb3csIHdlIGNhbiBleHBsb3JlIHdoZXRoZXIgdGhlcmUgYXJlIHBhdHRlcm5zIGluIHBhcnRpY2lwYW50InMgcmVzcG9uc2UgcmF0ZXMgdGhyb3VnaG91dCB0aGUgc3R1ZHkuIAoKSW4gdGhlIGZvbGxvd2luZyBwbG90IHlvdSBjYW4gc2VlIHRoZSBudW1iZXIgb2YgYmVlcHMgYW5zd2VyZWQgcGVyIHBhcnRpY2lwYW50LCBwZXIgb2JzZXJ2YXRpb24uIFRoZSB2ZXJ0aWNhbCBsaW5lcyBzZXBhcmF0ZSB0aGUgZGF5cy4gCgpQYXJ0aWNpcGFudCAxIGFuZCAxNiBzZWVtZWQgdG8gYW5zd2VyIG1vcmUgYmVlcHMgYXQgdGhlIHN0YXJ0IG9mIHRoZSBzdHVkeSB0aGFuIGxhdGVyIG9uLCBhbmQgdGhlcmUgYXJlIHNvbWUgY29uc2lkZXJhYmxlIGdhcHMgZm9yIHNvbWUgcGFydGljaXBhbnRzIChlLmcuIHBhcnRpY2lwYW50IDIpLiBCdXQgb3RoZXJ3aXNlIHRoZXJlInMgbm8gY2xlYXIgcGF0dGVybi4KCmBgYHtyIHNhbXBsaW5nIHNjaGVtZSBmb3IgYmVlcHN9CmRhdGEgfD4gCiAgZmlsdGVyKGFuc3dlcmVkKSB8PiAgIyBmaWx0ZXJpbmcgYnkgYW5zd2VyZWQgYmVlcHMsIGFub3RoZXIgb3B0aW9uIGlzIGRyb3BfbmEoc3RhcnQpCiAgZ2dwbG90KGFlcyh4ID0gb2JzX24sIHkgPSBmYWN0b3IocGFydGljaXBhbnQpKSkgKwogIGdlb21fcG9pbnQoCiAgICBzaXplID0gMS41LCAKICAgIGNvbCA9IGRhcmtfbXBhdGhfcGFsZXR0ZVsxXQogICkgKwogIGdlb21fdmxpbmUoCiAgICB4aW50ZXJjZXB0ID0gc2VxLmludChmcm9tID0gMTEuNSwgdG8gPSBtYXgoZGF0YSRvYnNfbiksIGJ5ID0gMTEpLCAKICAgIGNvbG9yID0gbXBhdGhfcGFsZXR0ZVszXSwgCiAgICBhbHBoYSA9IDAuNQogICkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJBbnN3ZXJlZCBiZWVwcyBwZXIgcGFydGljaXBhbnQiLAogICAgeCA9ICJPYnNlcnZhdGlvbiBudW1iZXIiLAogICAgeSA9ICJQYXJ0aWNpcGFudCIKICApCmBgYAoKIyMgRGVsYXkgdG8gc3RhcnQgYW5kIHRvIGZpbGwKCldlIHdpbGwgbm93IGxvb2sgYXQgdGhlIGRlbGF5IGJldHdlZW4gd2hlbiBwYXJ0aWNpcGFudHMgcmVjZWl2ZWQgYSBiZWVwIGFuZCB3aGVuIHRoZXkgYWN0dWFsbHkgYW5zd2VyZWQgaXQgKHRoZSBkZWxheSBpbiBzdGFydGluZyB0aGUgYmVlcCksIGFuZCBob3cgbG9uZyBpdCB0YWtlcyBwYXJ0aWNpcGFudHMgdG8gZmlsbCB0aGUgYmVlcC4gCgpXZSB3aWxsIHBsb3QgdGhlIGRlbGF5IHRvIHN0YXJ0IGFuZCB0byBmaWxsIHNlcGFyYXRpbmcgcGVyIHF1ZXN0aW9ubmFpcmUsIHRoaXMgaXMgYmVjYXVzZSB0aGUgdHdvIGRpZmZlcmVudCBxdWVzdGlvbm5haXJlcyBoYWQgYSBkaWZmZXJlbnQgZXhwaXJhdGlvbiB0aW1lLiBXZSBjYW4gc2VlIHRoYXQgbW9zdCBkZWxheXMgdG8gYW5zd2VyIGFyZSBxdWl0ZSBzbWFsbC4gU29tZSBhbnN3ZXJzIGZvciB0aGUgZXZlbmluZyBxdWVzdGlvbm5haXJlIHRvb2sgYSBsb25nIHRpbWUsIHRoaXMgd2FzIGJlY2F1c2UgdGhlIGV2ZW5pbmcgcXVlc3Rpb25uYWlyZSBoYWQgYW4gZXhwaXJhdGlvbiBvZiAzIGhvdXJzLCB3aGlsZSBwYXJ0aWNpcGFudHMgd2VyZSBvbmx5IGFibGUgdG8gYW5zd2VyIGJlZXBzIGZvciB0aGUgbWFpbiBxdWVzdGlvbm5haXJlIGluIHRoZSBmaXJzdCAzMCBtaW51dGVzLiAKCmBgYHtyIGRlbGF5IHRvIHN0YXJ0IGFuZCBmaWxsLCB3YXJuaW5nID0gRkFMU0V9CmRhdGEgPC0gZGF0YSB8PiAKICBtdXRhdGUoCiAgICBkZWxheV9zdGFydF9taW4gPSBkaWZmdGltZShzdGFydCwgc2VudCwgdW5pdHMgPSAibWlucyIpLAogICAgZGVsYXlfZW5kX21pbiA9IGRpZmZ0aW1lKHN0b3AsIHN0YXJ0LCB1bml0cyA9ICJtaW5zIikgCiAgKSAKCmRhdGEgfD4gCiAgZmlsdGVyKGFuc3dlcmVkKSAgfD4gCiAgZ2dwbG90KGFlcyh4ID0gZGVsYXlfc3RhcnRfbWluLCBmaWxsID0gcXVlc3Rpb25uYWlyZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwLCBhbHBoYSA9IDAuNywgcG9zaXRpb24gPSAiaWRlbnRpdHkiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGRhcmtfbXBhdGhfcGFsZXR0ZSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEZWxheSB0byBzdGFydCBwZXIgcXVlc3Rpb25uYWlyZSIsCiAgICB4ID0gIkRlbGF5IHRvIHN0YXJ0IChtaW51dGVzKSIsCiAgICB5ID0gIkNvdW50IgogICkKYGBgCgojIyBUaW1lIG9mIGFuc3dlcmVkL21pc3NlZCBiZWVwcwoKSGVyZSwgd2Ugd2lsbCBleHBsb3JlIHRoZSB0aW1lIG9mIGFuc3dlcmVkIGFuZCBtaXNzZWQgYmVlcHMuIElmIHRoZXNlIGhpc3RvZ3JhbXMgYXJlIHZlcnkgZGlmZmVyZW50LCBpdCBtYXkgaW5kaWNhdGUgdGhhdCBwYXJ0aWNpcGFudHMgYXJlIG1pc3NpbmcgYmVlcHMgYXQgY2VydGFpbiB0aW1lcyBvZiB0aGUgZGF5IChmb3IgZXhhbXBsZSwgaXQgY291bGQgYmUgdGhhdCBwYXJ0aWNpcGFudHMgbWlzcyBtb3JlIGJlZXBzIGluIHRoZSBtb3JuaW5nIHRoYW4gaW4gdGhlIGFmdGVybm9vbikuIAoKVGhlIHBsb3RzIGxvb2sgdmVyeSBzaW1pbGFyLiBXZSBjYW4gc2VlIHRoYXQgdGhlIGxhc3QgYmVlcHMgd2VyZSBzZW50IGF0IGFyb3VuZCAyMmgsIGJ1dCB0aGVyZSBhcmUgYW5zd2VyZWQgYmVlcHMgYXMgbGF0ZSBhcyAxaCBpbiB0aGUgbmlnaHQuIFRoaXMgaXMgYmVjYXVzZSB0aGUgZXhwaXJhdGlvbiBkYXRlIGZvciB0aGUgZXZlbmluZyBxdWVzdGlvbm5haXJlIHdhcyBzZXQgYXQgMyBob3Vycywgc28gc29tZSBwYXJ0aWNpcGFudHMgbWF5IGhhdmUgYW5zd2VyZWQgdGhlc2UgYmVlcHMgcXVpdGUgbGF0ZS4gVGhlIGV4cGlyYXRpb24gZm9yIHRoZSByZXN0IG9mIHRoZSBiZWVwcyB3YXMgYXJvdW5kIDMwIG1pbnV0ZXMuCgpgYGB7ciB0aW1lIG9mIGFuc3dlcmVkIGFuZCBtaXNzZWQgYmVlcHMsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIFRvIHVzZSB0aGUgZnVuY3Rpb24gYGRhdGVfYnJlYWtzKClgCmxpYnJhcnkoc2NhbGVzKQoKIyBIb3VyIG9mIGRheSBmcm9tIGFuc3dlcmVkIGJlZXBzCiMgRXh0cmFjdCB0aGUgaG91ciwgbWludXRlIGFuZCBzZWNvbmRzIGZyb20gdGhlIGBzdGFydGAgdGltZSB3aXRoIGBmb3JtYXQoKWAuCmRhdGEgfD4gCiAgZmlsdGVyKGFuc3dlcmVkKSB8PiAKICBtdXRhdGUoc3RhcnQgPSBmb3JtYXQoc3RhcnQsIGZvcm1hdCA9ICIlSDolTTolUyIpKSB8PiAKICBnZ3Bsb3QoYWVzKHggPSBobXMoc3RhcnQpKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDAsIGZpbGwgPSBkYXJrX21wYXRoX3BhbGV0dGVbMV0pICsKICBzY2FsZV94X3RpbWUoCiAgICBsaW1pdHMgPSBobXMoYygiMDA6MDA6MDAiLCAiMjQ6MDA6MDAiKSksIAogICAgYnJlYWtzID0gZGF0ZV9icmVha3MoIjIgaG91cnMiKSwKICAgIGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cmZ0aW1lKHgsIGZvcm1hdCA9ICIlSDolTSIpCiAgKSArCiAgbGFicygKICAgIHRpdGxlID0gIlRpbWUgb2YgYW5zd2VyZWQgYmVlcHMiLAogICAgeCA9ICJUaW1lIiwKICAgIHkgPSAiQ291bnQiCiAgKQoKIyBIb3VyIG9mIGRheSBvZiBtaXNzZWQgYmVlcHMKIyBFeHRyYWN0IHRoZSBob3VyLCBtaW51dGUgYW5kIHNlY29uZHMgZnJvbSB0aGUgYHNlbnRgIHRpbWUgd2l0aCBgZm9ybWF0KClgLgpkYXRhIHw+IAogIGZpbHRlcighYW5zd2VyZWQpIHw+IAogIG11dGF0ZShzZW50ID0gZm9ybWF0KHNlbnQsIGZvcm1hdCA9ICIlSDolTTolUyIpKSB8PiAKICBnZ3Bsb3QoYWVzKHggPSBobXMoc2VudCkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCwgZmlsbCA9IGRhcmtfbXBhdGhfcGFsZXR0ZVsyXSkgKwogIHNjYWxlX3hfdGltZSgKICAgIGxpbWl0cyA9IGhtcyhjKCIwMDowMDowMCIsICIyNDowMDowMCIpKSwgCiAgICBicmVha3MgPSBkYXRlX2JyZWFrcygiMiBob3VycyIpLAogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyZnRpbWUoeCwgZm9ybWF0ID0gIiVIOiVNIikKICApKwogIGxhYnMoCiAgICB0aXRsZSA9ICJUaW1lIG9mIG1pc3NlZCBiZWVwcyIsCiAgICB4ID0gIlRpbWUiLAogICAgeSA9ICJDb3VudCIKICApCmBgYAoKIyMgQ29tcGxpYW5jZSByYXRlCgpDb21wbGlhbmNlIGlzIHRoZSBwcm9wb3J0aW9uIG9mIGFuc3dlcmVkIGJlZXBzLiBJdCBpcyBpbXBvcnRhbnQgdG8gaGF2ZSBhIGxvb2sgYXQgdGhpcyBtZXRyaWMsIHRvIGlkZW50aWZ5IHBhcnRpY2lwYW50cyB0aGF0IGhhdmUgbWlzc2VkIGEgbG90IG9mIGJlZXBzLiBXZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgcmVzcG9uc2VfcmF0ZWAgZnJvbSB0aGUgYG1wYXRocmAgcGFja2FnZSB0byBjYWxjdWxhdGUgdGhlIHJlc3BvbnNlIHJhdGUuIAoKYGBge3IgY29tcGxpYW5jZSByYXRlLCBtZXNzYWdlID0gRkFMU0V9CiMgR2V0IHRoZSByZXNwb25zZSByYXRlcwpyZXNwb25zZV9yYXRlcyA8LSByZXNwb25zZV9yYXRlKAogIGRhdGEgPSBkYXRhLAogIHZhbGlkX2NvbCA9IGFuc3dlcmVkLCAKICBwYXJ0aWNpcGFudF9jb2wgPSBwYXJ0aWNpcGFudAopCgpyZXNwb25zZV9yYXRlcyB8PgogIGdyb3VwX2J5KHBhcnRpY2lwYW50KSB8PgogIGdncGxvdChhZXMoeCA9IHBhcnRpY2lwYW50LCB5ID0gcmVzcG9uc2VfcmF0ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9IGRhcmtfbXBhdGhfcGFsZXR0ZVszXSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJDb21wbGlhbmNlIHBlciBwYXJ0aWNpcGFudCIsCiAgICB4ID0gIlBhcnRpY2lwYW50IiwKICAgIHkgPSAiQ29tcGxpYW5jZSAocHJvcG9ydGlvbiBvZiBhbnN3ZXJlZCBiZWVwcykiCiAgKQpgYGAKCkFkZGl0aW9uYWxseSwgd2UgY2FuIGp1c3QgcHJpbnQgYSBzdW1tYXJ5IG9mIGVhY2ggcGFydGljaXBhbnQgYW5kIHRoZWlyIGNvbXBsaWFuY2U6CgpgYGB7ciBwcmludCByZXNwb25zZSByYXRlc30KcHJpbnQocmVzcG9uc2VfcmF0ZXMpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHNvbWUgcGFydGljaXBhbnRzIGhhdmUgYSB2ZXJ5IGxvdyBjb21wbGlhbmNlIHJhdGUgKGUuZy4gcGFydGljaXBhbnQgMSB3aXRoIGEgY29tcGxpYW5jZSBvZiAxMSUpLiBXZSBtYXkgd2FudCB0byBleGNsdWRlIHRoZXNlIHBhcnRpY2lwYW50cyBmcm9tIHRoZSBhbmFseXNpcy4KCmBgYHtyIGZpbHRlciBvdXQgcGFydGljaXBhbnRzIHdpdGggbG93IGNvbXBsaWFuY2V9CiMgUmVtb3ZlIHBhcnRpY2lwYW50cyB3aXRoIGEgcmVzcG9uc2UgcmF0ZSBsb3dlciB0aGFuIDMwJQpsb3dfcmVzcG9uc2UgPC0gcmVzcG9uc2VfcmF0ZXMgfD4KICBmaWx0ZXIocmVzcG9uc2VfcmF0ZSA8IDAuMykgfD4KICBwdWxsKCJwYXJ0aWNpcGFudCIpICMgUHVsbCBvdXQgYSB2YXJpYWJsZSBhcyBhIHZlY3RvcgoKZGF0YSA8LSBkYXRhIHw+CiAgZmlsdGVyKCEocGFydGljaXBhbnQgJWluJSBsb3dfcmVzcG9uc2UpKQpgYGAKCiMjIyBEZXRlY3RpbmcgZGlzaG9uZXN0eQoKSW4gY2FzZXMgd2hlcmUgcGFydGljaXBhbnRzIHJlY2VpdmUgaW5jZW50aXZlcyBiYXNlZCBvbiBob3cgbWFueSBxdWVzdGlvbm5haXJlcyB0aGV5IGZpbGxlZCwgdGhleSBjb3VsZCBtYW51YWxseSBjaGFuZ2UgdGhlIHRpbWUgb24gdGhlaXIgcGhvbmUgdG8gZmlsbCBpbiBvbGRlciBxdWVzdGlvbm5haXJlcy4gSW4gb3JkZXIgdG8gZGV0ZWN0IHRoaXMsIHdlIGhhdmUgdGhlIGNvbHVtbiBgcGhvbmVfc2VydmVyX29mZnNldGAsIHdoaWNoIGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHBob25lIHRpbWUgYW5kIHRoZSBzZXJ2ZXIgdGltZS4gCgpJZiBwYXJ0aWNpcGFudHMgY2hhbmdlIHRoZSB0aW1lIG9uIHRoZWlyIHBob25lLCB0aGlzIGNvbHVtbiB3aWxsIHNob3cgYSBsYXJnZSBkaWZmZXJlbmNlLiBXZSBjYW4gY2hlY2sgaWYgdGhlcmUgYXJlIGFueSBwYXJ0aWNpcGFudHMgd2l0aCBhIGxhcmdlIGRpZmZlcmVuY2UgaW4gdGhlIGBwaG9uZV9zZXJ2ZXJfb2Zmc2V0YCBjb2x1bW4uIAoKYGBge3IgY2hlY2sgcGhvbmUtc2VydmVyIG9mZnNldCwgd2FybmluZz1GQUxTRX0KZGF0YSB8PgogIG11dGF0ZShwaG9uZV9zZXJ2ZXJfb2Zmc2V0ID0gYWJzKHBob25lX3NlcnZlcl9vZmZzZXQgLyA2MCkpIHw+CiAgZ2dwbG90KGFlcyh4ID0gYWJzKHBob25lX3NlcnZlcl9vZmZzZXQpKSkgKwogIGdlb21faGlzdG9ncmFtKAogICAgYmlucyA9IDEwMCwgCiAgICBhbHBoYSA9IDAuNywgCiAgICBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIAogICAgZmlsbCA9IG1wYXRoX3BhbGV0dGVbMV0KICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiRGlmZmVyZW5jZSBiZXR3ZWVuIHBob25lIGFuZCBzZXJ2ZXIgdGltZSIsCiAgICB4ID0gIlBob25lLXNlcnZlciBvZmZzZXQgKG1pbnV0ZXMpIiwKICAgIHkgPSAiQ291bnQiCiAgKQpgYGAKCldlIGNhbiBzZWUgdGhhdCB0aGUgbWF4aW11bSBkaWZmZXJlbmNlIGlzIGFyb3VuZCAzIG1pbnV0ZXMuIFNpbmNlIHRoaXMgaXMgbm90IGEgbGFyZ2UgZGlmZmVyZW5jZSwgaXQgc2VlbXMgbGlrZSBwYXJ0aWNpcGFudHMgZGlkbiJ0IGNoYW5nZSB0aGUgdGltZSBvbiB0aGVpciBwaG9uZSBkdXJpbmcgdGhlIHN0dWR5LgoKIyMgQ29uY2x1c2lvbnM6IFBhcnRpY2lwYW50cyByZXNwb25zZSBiZWhhdmlvcnMKCkluIHRoaXMgc2VjdGlvbiwgd2UgZXhhbWluZWQgcGFydGljaXBhbnRzIiByZXNwb25zZSBiZWhhdmlvcnMgYW5kIGlkZW50aWZpZWQgc29tZSBwYXR0ZXJucy4gCkZvciBleGFtcGxlLCB3ZSBmb3VuZCBhbmQgcmVtb3ZlZCBwYXJ0aWNpcGFudHMgd2l0aCBhIHZlcnkgbG93IGNvbXBsaWFuY2UgcmF0ZS4KCkZvciBtb3JlIGd1aWRlbGluZXMgb24gaG93IHRvIGNvbmR1Y3Qgc2ltaWxhciBjaGVja3MsIHNlZSBbRVNNIFByZXByb2Nlc3NpbmcgR2FsbGVyeV0oaHR0cHM6Ly9wcmVwcm9jZXNzLmVzbXRvb2xzLmNvbS8pLgoKIyBTdGVwIDQ6IENvbXB1dGUgYW5kIHRyYW5zZm9ybSB2YXJpYWJsZXMKCkluIHRoaXMgc3RlcCB3ZSB3aWxsIGNvbXB1dGUgaW1wb3J0YW50IHZhcmlhYmxlcyB0byBwcmVwYXJlIHRoZSBkYXRhIGZvciBvdXIgYW5hbHlzaXMuIAoKV2Ugd2lsbCBjb25kdWN0IHRoZSBmb2xsb3dpbmcgdHdvIGFuYWx5c2VzOgoKMS4gKipBbmFseXNpcyAxKio6IERvIHBhcnRpY2lwYW50cyBoYXZlIG1vcmUgbmVnYXRpdmUgYWZmZWN0IHdoZW4gdGhleSBhcmUgYWxvbmU/CjIuICoqQW5hbHlzaXMgMioqOiBJcyB0aGUgZXZlbmluZy1yZXBvcnRlZCBhbnhpZXR5IGxldmVsIHByZWRpY3RlZCB3ZWxsIHdpdGggYSBjb21iaW5hdGlvbiBvZiB0aGUgbWVhbiBhbnhpZXR5IGxldmVscyBhbmQgdGhlIG1heGltdW0gYW54aWV0eSBsZXZlbCBleHBlcmllbmNlZCB0aHJvdWdob3V0IHRoZSBkYXk/CgojIyBEYXRhIHByZXBhcmF0aW9uIGZvciBhbmFseXNpcyAxCiMjIyBDb21wdXRpbmcgYWZmZWN0IHNjb3JlcwoKRm9yIHRoZSBmaXJzdCBhbmFseXNpcywgd2UgbmVlZCB0byBjb21wdXRlIGEgbWVhc3VyZSBvZiBuZWdhdGl2ZSBhZmZlY3QuIFRoaXMgd2lsbCBiZSB0aGUgbWVhbiBvZiB0aGUgc2FkLCBhbmdyeSwgYW54aW91cywgYW5kIHRpcmVkIHNsaWRlcnMuIAoKYGBge3IgY29tcHV0ZSBuZWdhdGl2ZSBhZmZlY3Qgc2NvcmV9CmRhdGEgPC0gZGF0YSB8PiAKICBtdXRhdGUobmVnYXRpdmVfYWZmZWN0ID0gcm93TWVhbnMocGljaygKICAgICJzbGlkZXJfc2FkIiwgInNsaWRlcl9hbmdyeSIsICJzbGlkZXJfYW54aW91cyIsICJzbGlkZXJfdGlyZWQiCiAgKSkpCmBgYAoKIyMjIENvbXB1dGluZyBiaW5hcnkgdmFyaWFibGVzIGZyb20gbXVsdGlwbGUgY2hvaWNlIHF1ZXN0aW9ucwoKRm9yIHRoZSBmaXJzdCBhbmFseXNpcywgd2UgYWxzbyBuZWVkIHRvIGNyZWF0ZSBhIGJpbmFyeSB2YXJpYWJsZSB0aGF0IGluZGljYXRlcyB3aGV0aGVyIHRoZSBwYXJ0aWNpcGFudCB3YXMgYWxvbmUgb3Igbm90LiBXZSBjYW4gdXNlIHRoZSBgY29tcGFueV9zdHJpbmdgIHZhcmlhYmxlIHRvIGNyZWF0ZSBpdC4gCgpUaGUgYGNvbXBhbnlfc3RyaW5nYCBjb2x1bW4gY29udGFpbnMgdGhlIHJlc3BvbnNlIG9wdGlvbnMgdGhhdCBpbmRpY2F0ZSB0aGUgcGFydGljaXBhbnQncyBzb2NpYWwgc3RhdHVzOiAKCiogT3B0aW9uIDE6ICJBbG9uZSIKKiBPcHRpb24gMjogIldpdGggb25lIHBlcnNvbiBJIGtub3ciCiogT3B0aW9uIDM6ICJXaXRoIG11bHRpcGxlIHBlb3BsZSBJIGtub3ciCiogT3B0aW9uIDQ6ICJXaXRoIG9uZSBvciBtb3JlIHBlb3BsZSBJIGRvbiJ0IGtub3ciCgpXZSB3aWxsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYGFsb25lYCB0aGF0IGlzIGBUUlVFYCBpZiB0aGUgcGFydGljaXBhbnQgd2FzIGFsb25lLCBhbmQgYEZBTFNFYCBpZiB0aGV5IHdlcmUgbm90LiAKCmBgYHtyIGNyZWF0ZSBhbG9uZSB2YXJpYWJsZX0KZGF0YSA8LSBkYXRhIHw+CiAgbXV0YXRlKGFsb25lID0gY29tcGFueV9zdHJpbmcgPT0gIkFsb25lIikgCmBgYAoKVGhpcyBpcyB0aGUgb25seSB0cmFuc2Zvcm1hdGlvbiB3ZSBuZWVkIGZvciB0aGUgZmlyc3QgYW5hbHlzaXMuIFdlIGNhbiBzYXZlIGF3YXkgdGhlIGRhdGEgZnJvbSB0aGlzIGFuYWx5c2lzIHRvIGBkYXRhX2FuYWx5c2lzXzFgLCB3aGVyZSB3ZSBhbHNvIGZpbHRlciBvdXQgdW5hbnN3ZXJlZCBiZWVwcyAob3IgYmVlcHMgdGhhdCBkbyBub3QgY29ycmVzcG9uZCB0byB0aGUgbWFpbiBxdWVzdGlvbm5haXJlKSBhbmQgb25seSBrZWVwIHRoZSB2YXJpYWJsZXMgd2UgbmVlZCBmb3IgdGhlIGFuYWx5c2lzLiAKCmBgYHtyIGNyZWF0ZSBkYXRhIGZvciBhbmFseXNpcyAxfQpkYXRhX2FuYWx5c2lzXzEgPC0gZGF0YSB8PiAKICBmaWx0ZXIocXVlc3Rpb25uYWlyZSA9PSAibWFpbl9xdWVzdGlvbm5haXJlIiAmIGFuc3dlcmVkKSB8PiAKICBzZWxlY3QoInBhcnRpY2lwYW50IiwgIm5lZ2F0aXZlX2FmZmVjdCIsICJhbG9uZSIpCmBgYAoKIyMgRGF0YSBwcmVwYXJhdGlvbiBmb3IgYW5hbHlzaXMgMgojIyMgRmlsdGVyaW5nIG91dCBwYXJ0aWNpcGFudHMgYW5kIGRheXMgd2l0aCBzbWFsbCByZXNwb25zZSByYXRlCkZvciB0aGUgc2Vjb25kIGFuYWx5c2lzLCB3ZSB3aWxsIGVsaW1pbmF0ZSBwYXJ0aWNpcGFudCJzIGRhdGEgaWYgdGhleSBoYWQgbGVzcyB0aGFuIDUwJSBjb21wbGlhbmNlIHJhdGU6CgpgYGB7ciBmaW5kIHBhcnRpY2lwYW50cyB3aXRoIGxvdyBjb21wbGlhbmNlfQojIENhbGN1bGF0ZSB3aGljaCBwYXJ0aWNpcGFudHMgaGF2ZSBhIGNvbXBsaWFuY2UgcmF0ZSBvZiBsZXNzIHRoYW4gNTAlCnBhcnRpY2lwYW50c190b19ybSA8LSByZXNwb25zZV9yYXRlKAogIGRhdGEgPSBkYXRhLCAKICB2YWxpZF9jb2wgPSBhbnN3ZXJlZCwgCiAgcGFydGljaXBhbnRfY29sID0gcGFydGljaXBhbnQKKSB8PgogIGZpbHRlcihyZXNwb25zZV9yYXRlIDwgMC41KSB8PiAKICBwdWxsKCJwYXJ0aWNpcGFudCIpCgojIHRoZXNlIGFyZSB0aGUgcGFydGljaXBhbnRzIHdlIHdpbGwgcmVtb3ZlIGZvciB0aGUgMm5kIGFuYWx5c2lzCnByaW50KHBhcnRpY2lwYW50c190b19ybSkgCmBgYApOb3cgdGhhdCB3ZSBrbm93IHdoaWNoIHBhcnRpY2lwYW50cyB0byByZW1vdmUsIHdlIGp1c3QgZmlsdGVyIHRoZW0gb3V0IGFuZCBzYXZlIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZSB0byBgZGF0YV9hbmFseXNpc18yYC4KCmBgYHtyIGZpbHRlciBvdXQgcGFydGljaXBhbnRzIGZvciBhbmFseXNpcyAyfQojIGZpbHRlcnMgb3V0IHBhcnRpY2lwYW50cyB0aGF0IGFyZSBwcmVzZW50IGluIHBhcnRpY2lwYW50c190b19ybQpkYXRhX2FuYWx5c2lzXzIgPC0gZGF0YSB8PgogIGZpbHRlcighKHBhcnRpY2lwYW50ICVpbiUgcGFydGljaXBhbnRzX3RvX3JtKSkgCmBgYAoKQWxzbywgZm9yIGV2ZXJ5IHBhcnRpY2lwYW50LCBpZiB0aGV5IGFuc3dlcmVkIGxlc3MgdGhhbiA1MCUgb2YgdGhlIGJlZXBzIG9uIGEgZ2l2ZW4gZGF5LCB3ZSB3aWxsIHJlbW92ZSB0aGF0IGRheSJzIGRhdGEgZnJvbSB0aGUgYW5hbHlzaXMuIFRoaXMgaXMgaW1wb3J0YW50IHNpbmNlIHdlIHdpbGwgdGFrZSB0aGUgbWVhbiwgYW5kIHdlIHdvdWxkIG5vdCB3YW50IHRvIGFuYWx5emUgbWVhbnMgb2YgdmVyeSBmZXcgdmFsdWVzLgoKYGBge3IgZmlsdGVyIG91dCBkYXlzIHdpdGggbG93IGNvbXBsaWFuY2UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpkYXlzX2JlbG93XzUwIDwtIGRhdGFfYW5hbHlzaXNfMiB8PgogIGdyb3VwX2J5KHBhcnRpY2lwYW50LCBkYXlfbikgfD4gCiAgc3VtbWFyaXplKG5hX3Byb3AgPSBzdW0oYW5zd2VyZWQpIC8gbWF4KG9ic19uX2RheSkpIHw+IAogIGZpbHRlcihuYV9wcm9wIDwgMC41KQoKIyBVc2UgYW4gYW50aSBqb2luIHRvIHJlbW92ZSBlbGVtZW50cyBmcm9tIGBkYXRhX2FuYWx5c2lzXzJgIHRoYXQgbWF0Y2ggdGhvc2UgaW4gCiMgYGRheXNfYmVsb3dfNTBgCmRhdGFfYW5hbHlzaXNfMiA8LSBkYXRhX2FuYWx5c2lzXzIgfD4KICBhbnRpX2pvaW4oZGF5c19iZWxvd181MCwgYnkgPSBjKCJwYXJ0aWNpcGFudCIsICJkYXlfbiIpKSAKYGBgCgojIyMgQ29tcHV0aW5nIHRoZSBtYXhpbXVtIGFuZCBtZWFuIGFueGlldHkgbGV2ZWwKCldlIHdpbGwgY2FsY3VsYXRlIHRoZSBtYXhpbXVtIGFuZCB0aGUgbWVhbiBhbnhpZXR5IGxldmVsIChpbiBgc2xpZGVyX2FueGlvdXNgKSBleHBlcmllbmNlZCBieSBlYWNoIHBhcnRpY2lwYW50IGluIGVhY2ggZGF5LiBXZSBjYW4gZG8gdGhpcyB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZTogCgpgYGB7ciBjb21wdXRlIG1heCBhbmQgbWVhbiBhbnhpZXR5IGxldmVsfQpkYXRhX2FuYWx5c2lzXzIgPC0gZGF0YV9hbmFseXNpc18yIHw+CiAgZ3JvdXBfYnkocGFydGljaXBhbnQsIGRheV9uKSB8PgogIG11dGF0ZSgKICAgIG1heF9hbnhpb3VzID0gbWF4KHNsaWRlcl9hbnhpb3VzLCBuYS5ybSA9IFRSVUUpLAogICAgbWVhbl9hbnhpb3VzID0gbWVhbihzbGlkZXJfYW54aW91cywgbmEucm0gPSBUUlVFKQogICkgfD4gCiAgdW5ncm91cCgpIHw+CiAgZmlsdGVyKHF1ZXN0aW9ubmFpcmUgPT0gImV2ZW5pbmdfcXVlc3Rpb25uYWlyZSIgJiBhbnN3ZXJlZCkgfD4KICBzZWxlY3QoInBhcnRpY2lwYW50IiwgImRheV9uIiwgImV2ZW5pbmdfc2xpZGVyX2FueGlvdXMiLCAibWF4X2FueGlvdXMiLCAibWVhbl9hbnhpb3VzIikgCmBgYAoKIyBTdGVwIDU6IERhdGEgZXhwbG9yYXRpb24KCk5vdywgd2Ugd2lsbCBleHBsb3JlIHRoZSBkYXRhIGEgYml0IG1vcmUgdG8gZ2V0IHRvIGtub3cgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgY2hhcmFjdGVyaXN0aWNzIG9mIHNvbWUgaW1wb3J0YW50IHZhcmlhYmxlcy4gCgojIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIG5lZ2F0aXZlIGFmZmVjdCB2YXJpYWJsZXMKU2luY2Ugd2UgYXJlIHBvb2xpbmcgdG9nZXRoZXIgYWxsIHRoZSBuZWdhdGl2ZSBhZmZlY3QgdmFyaWFibGVzIGludG8gYSBjb21wb3NpdGUgdmFyaWFibGUgKG5lZ2F0aXZlIGFmZmVjdCksIGl0InMgaW50ZXJlc3RpbmcgdG8gc2VlIGhvdyByZWxhdGVkIHRoZXkgYXJlLiBXZSBjYW4gcXVpY2tseSB2aXN1YWxpemUgdGhlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRoZXNlIHZhcmlhYmxlcyBpbiB0aGUgcGxvdCBiZWxvdzogCgpgYGB7ciBjb3JyZWxhdGlvbiBiZXR3ZWVuIG5lZ2F0aXZlIGFmZmVjdCB2YXJpYWJsZXMsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeShHR2FsbHkpCgpkYXRhIHw+IAogIHNlbGVjdCgic2xpZGVyX2FueGlvdXMiLCAic2xpZGVyX3NhZCIsICJzbGlkZXJfYW5ncnkiLCJzbGlkZXJfdGlyZWQiKSB8PiAKICBkcm9wX25hKCkgfD4gIyBEcm9wIGFsbCBtaXNzaW5nIHZhbHVlcwogIGdncGFpcnMoKQpgYGAKCkFzIGV4cGVjdGVkLCBjb3JyZWxhdGlvbnMgYXJlIHBvc2l0aXZlLCBidXQgbW9zdCBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0aGUgdmFyaWFibGVzIGFyZSByYXRoZXIgd2Vhay4gCgpBbG9uZyB0aGUgZGlhZ29uYWwsIHdlIGNhbiBzZWUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdmFyaWFibGVzLiBNb3N0IGRpc3RyaWJ1dGlvbnMgYXJlIGNsZWFybHkgc2tld2VkIHRvIHRoZSByaWdodC4gV2hpY2ggbWVhbnMgdGhhdCBtb3N0IG9mIHRoZSB0aW1lLCBwYXJ0aWNpcGFudHMgcmVwb3J0IGxvdyBsZXZlbHMgb2YgYW54aWV0eSwgc2FkbmVzcyBhbmQgYW5nZXIuIEhvd2V2ZXIsIHBhcnRpY2lwYW50cyByZXBvcnQgaGlnaGVyIGxldmVscyBvZiB0aXJlZG5lc3MuIAoKIyMgQWxvbmUgdnMuIG5vdCBhbG9uZQoKV2UgY291bGQgbG9vayBhdCBob3cgb2Z0ZW4gcGFydGljaXBhbnRzIHJlcG9ydGVkIGJlaW5nIGFsb25lLiBXZSBjYW4gc2VlIHRoYXQgdGhlIHByb3BvcnRpb24gb2YgdGltZSBhbG9uZSBncmVhdGx5IHZhcmllcyBiZXR3ZWVuIGluZGl2aWR1YWxzLiBTb21lIHBhcnRpY2lwYW50cyByZXBvcnQgYmVpbmcgYWxvbmUgYWxtb3N0IDkwJSBvZiB0aGUgdGltZSwgd2hpbGUgc29tZSBvdGhlcnMgYXJlIGFjY29tcGFuaWVkIG1vc3RseSA5MCUgb2YgdGhlIHRpbWUuIAoKYGBge3IgYWxvbmUgdnMuIG5vdCBhbG9uZSBwbG90fQphbG9uZV9kYXRhIDwtIGRhdGEgfD4KICBkcm9wX25hKGFsb25lKSB8PiAKICBtdXRhdGUoY29tcGFueV9zdHJpbmcgPSBmYWN0b3IoY29tcGFueV9zdHJpbmcsIGxldmVscyA9IGMoCiAgICAiQWxvbmUiLCAKICAgICJXaXRoIG9uZSBwZXJzb24gSSBrbm93IiwgCiAgICAiV2l0aCBtdWx0aXBsZSBwZW9wbGUgSSBrbm93IiwgCiAgICAiV2l0aCBvbmUgb3IgbW9yZSBwZW9wbGUgSSBkb24ndCBrbm93IgogICkpKSB8PiAKICBjb3VudChwYXJ0aWNpcGFudCwgY29tcGFueV9zdHJpbmcpCiAgCmFsb25lX2RhdGEgfD4gCiAgZ2dwbG90KGFlcygKICAgIHggPSBwYXJ0aWNpcGFudCwKICAgIHkgPSBuLAogICAgZmlsbCA9IGNvbXBhbnlfc3RyaW5nCiAgKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiLCBzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKAogICAgbmFtZSA9ICJUeXBlIG9mIGNvbXBhbnkiLAogICAgdmFsdWVzID0gYyhtcGF0aF9wYWxldHRlW2MoMSwzKV0sICIjREI4OTA4IiwgbXBhdGhfcGFsZXR0ZVsyXSkKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAiUHJvcG9ydGlvbiBvZiB0aW1lIGFsb25lIG9yIGFjY29tcGFuaWVkIiwKICAgIHggPSAiUGFydGljaXBhbnQiLAogICAgeSA9ICJDb3VudCIKICApCmBgYAoKRm9yIHRoZSBhbmFseXNpcywgd2Ugd2lsbCBwb29sIHRvZ2V0aGVyIHJlcG9ydHMgb2YgYmVpbmcgd2l0aCBtdWx0aXBsZSBwZW9wbGUsIG9yIGJlaW5nIHdpdGggb25seSBvbmUgcGVyc29uLiBXZSB3aWxsIGFsc28gY29uc2lkZXIgYmVpbmcgd2l0aCBvbmUgb3IgbW9yZSBwZW9wbGUgdGhhdCB0aGUgcGFydGljaXBhbnQgZG9lcyBub3Qga25vdyBhcyBiZWluZyBhY2NvbXBhbmllZC4gCgojIyBFbW90aW9uIHZhcmlhYmxlcwoKV2UgY2FuIGFsc28gaGF2ZSBhIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZW1vdGlvbiBzbGlkZXJzLiBXZSBjYW4gbWFpbmx5IHNlZSB0aGF0IHRoZSBuZWdhdGl2ZSBlbW90aW9ucyAoYW5ncnksIGFueGlvdXMsIHNhZCBhbmQgdGlyZWQpIHRlbmQgdG8gYmUgc2tld2VkIHRvIHRoZSByaWdodCwgd2hpY2ggbWVhbnMgdGhhdCBtb3N0IG9mIHRoZSB0aW1lIHBhcnRpY2lwYW50cyByZXBvcnQgbG93IGxldmVscyBvZiB0aGVzZSBlbW90aW9ucy4gIlRpcmVkIiBzZWVtcyB0byBiZSBhbiBleGNlcHRpb24gdG8gdGhpcyB0cmVuZC4gTWVhbndoaWxlLCB0aGUgcG9zaXRpdmUgZW1vdGlvbnMgKGhhcHB5LCByZWxheGVkIGFuZCBlbmVyZ2V0aWMpIHRlbmQgdG8gYmUgc2tld2VkIHRvIHRoZSBsZWZ0LCB3aGljaCBtZWFucyB0aGF0IG1vc3Qgb2YgdGhlIHRpbWUgcGFydGljaXBhbnRzIHJlcG9ydCBoaWdoIGxldmVscyBvZiB0aGVzZSBlbW90aW9ucy4gCgpgYGB7ciBwbG90IGRpc3RyaWJ1dGlvbiBvZiBlbW90aW9uIHZhcmlhYmxlcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRhdGEgfD4gCiAgcGl2b3RfbG9uZ2VyKCJzbGlkZXJfaGFwcHkiOiJzbGlkZXJfdGlyZWQiKSB8PiAKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IGFmdGVyX3N0YXQoZGVuc2l0eSkpLCBhbHBoYSA9IDAuNykgKyAKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsKICBmYWNldF93cmFwKHZhcnMobmFtZSksIHNjYWxlcyA9ICJmcmVlX3kiKQpgYGAKCiMgU3RlcCA2OiBBbmFseXNpcwojIyBRdWVzdGlvbiAxOiBEbyBwYXJ0aWNpcGFudHMgaGF2ZSBtb3JlIG5lZ2F0aXZlIGFmZmVjdCB3aGVuIHRoZXkgYXJlIGFsb25lPwoKV2Ugd2lsbCBhc3Nlc3MgdGhpcyBxdWVzdGlvbiB3aXRoIGEgbWl4ZWQgbW9kZWwsIHRvIGFjY291bnQgZm9yIHRoZSByYW5kb20gZWZmZWN0IG9mIHBhcnRpY2lwYW50cy4gV2Ugd2lsbCB1c2UgdGhlIGZvbGxvd2luZyBmb3JtdWxhOiAKCiQkXHRleHR7bmVnYXRpdmUgYWZmZWN0IH4gYWxvbmUgKyAoMSArIGFsb25lIHwgcGFydGljaXBhbnQpfSQkCldpdGggdGhpcyBmb3JtdWxhLCB3ZSBzcGVjaWZ5IGFuIGludGVyY2VwdCBmb3IgZXZlcnkgcGFydGljaXBhbnQsIGFzIHdlbGwgYXMgcmFuZG9tIHNsb3BlcyBmb3IgYGFsb25lYC4KCmBgYHtyIGFuYWx5c2lzIDEgbW9kZWwsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeShsbWU0KQoKbW9kZWxfMSA8LSBsbWVyKAogIGZvcm11bGEgPSBuZWdhdGl2ZV9hZmZlY3QgfiBhbG9uZSArICgxICsgYWxvbmUgfCBwYXJ0aWNpcGFudCksIAogIGRhdGEgPSBkYXRhX2FuYWx5c2lzXzEKKQpgYGAKCkJlZm9yZSBsb29raW5nIGF0IHRoZSByZXN1bHRzLCB3ZSBzaG91bGQgY2hlY2sgdGhhdCBvdXIgbWl4ZWQgbW9kZWwicyBhc3N1bXB0aW9ucyBhcmUgbWV0LiBJbiBvdXIgY2FzZSwgaXQgZG9lcyBub3QgbWFrZSBzZW5zZSB0byBsb29rIGF0IGxpbmVhcml0eSBiZWNhdXNlIG91ciBvbmx5IHByZWRpY3RvciAoYGFsb25lYCkgaXMgYSBiaW5hcnkgdmFyaWFibGUuIEhvd2V2ZXIsIHdlIGNhbiBjaGVjayBmb3IgaG9tb3NjZWRhc3RpY2l0eSBhbmQgbm9ybWFsaXR5IG9mIHJlc2lkdWFsczogCgoqKkhvbW9zY2VkYXN0aWNpdHkqKjogV2UgY2FuIGNoZWNrIGZvciBob21vc2NlZGFzdGljaXR5IChhbHNvIGtub3duIGFzIHRoZSBob21vZ2VuZWl0eSBvZiB2YXJpYW5jZSkgYnkgdXNpbmcgYSBmdW5jdGlvbiBvZiB0aGUgYHBlcmZvcm1hbmNlYCBwYWNrYWdlIHdoaWNoIGlzIHBhcnQgb2YgdGhlIGxhcmdlciBmcmFtZXdvcmsgb2YgYGVhc3lzdGF0c2AuIFdoaWxlIHRoZSBCcmV1c2NoLVBhZ2FuIHRlc3QgdGhhdCBpcyBydW4gYnkgYGNoZWNrX2hldGVyb3NjZWRhc3RpY2l0eSgpYCB3YXMgc2lnbmlmaWNhbnQgKCRwPC4wMDEkKSwgYSB2aXN1YWwgaW5zcGVjdCB0ZWxscyB1cyB0aGF0IHRoZXJlIGlzIG9ubHkgYSB2ZXJ5IG1pbm9yIG92ZXJhbGwgdHJlbmQgaW4gdGhlIHZhcmlhbmNlIG9mIHRoZSByZXNpZHVhbHMsIHdoaWNoIGlzIHVubGlrZWx5IHRvIGluZmx1ZW5jZSB0aGUgcmVzdWx0cy4KCmBgYHtyIGhvbW9zY2VkYXN0aWNpdHkgYW5hbHlzaXMgMSwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHBlcmZvcm1hbmNlKQoKY2hlY2tfaGV0ZXJvc2NlZGFzdGljaXR5KG1vZGVsXzEpCnBsb3QoY2hlY2tfaGV0ZXJvc2NlZGFzdGljaXR5KG1vZGVsXzEpKQpgYGAKCioqTm9ybWFsaXR5Kio6IFdlIGNhbiBhbHNvIGNoZWNrIGZvciBub3JtYWxpdHkgb2YgdGhlIHJlc2lkdWFscyBieSB1c2luZyB0aGUgYGNoZWNrX25vcm1hbGl0eSgpYCBmdW5jdGlvbi4gVGhlIFNoYXBpcm8tV2lsayB0ZXN0IHRoYXQgaXMgcnVuIGJ5IHRoaXMgZnVuY3Rpb24geWllbGRlZCBhIHNpZ25pZmljYW50IHJlc3VsdCAoJHA8LjAwMSQpLiBBIHZpc3VhbCBpbnNwZWN0aW9uIHNob3dzIHRoYXQgdGhlIHJlc2lkdWFscyBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGZvciB0aGUgbG93ZXIgcXVhbnRpbGVzIGFuZCBhcmUgc2xpZ2h0bHkgaGlnaGVyIGluIHRoZSB1cHBlciBxdWFudGlsZXMuIAoKYGBge3Igbm9ybWFsaXR5IGFuYWx5c2lzIDF9CmNoZWNrX25vcm1hbGl0eShtb2RlbF8xKQpwbG90KGNoZWNrX25vcm1hbGl0eShtb2RlbF8xKSkKYGBgCgpXZSBjYW4gbm93IGNoZWNrIHRoZSByZXN1bHRzIG9mIHRoZSBtb2RlbDoKCmBgYHtyIHJlc3VsdHMgYW5hbHlzaXMgMSwgbWVzc2FnZSA9IEZBTFNFfQpzdW1tYXJ5KG1vZGVsXzEpIAoKIyBDaGktc3F1YXJlIHRlc3QgdG8gc2VlIGlmIHRoZSBlZmZlY3RzIGFyZSBkaWZmZXJlbnQgZnJvbSAwCmxpYnJhcnkoY2FyKQpBbm92YShtb2RlbF8xKQpgYGAKCkZyb20gdGhlIHJlc3VsdHMsIHdlIGNhbiBzZWUgdGhhdCB0aGUgZWZmZWN0IG9mIGJlaW5nIGFsb25lIG9uIG5lZ2F0aXZlIGFmZmVjdCBpcyBub3Qgc2lnbmlmaWNhbnQgKCpwKiA9IGByIHJvdW5kKEFub3ZhKG1vZGVsXzEpWzNdLDIpYCkuIEl0IGRvZXMgbm90IHNlZW0gbGlrZSBwYXJ0aWNpcGFudHMgaGF2ZSBtb3JlIG5lZ2F0aXZlIGFmZmVjdCB3aGVuIHRoZXkgYXJlIGFsb25lLiBIb3dldmVyLCB3ZSBhcmUgdXNpbmcgYSB2ZXJ5IHNtYWxsIGRhdGFzZXQsIHdoaWNoIHJlc3VsdHMgaW4gdmVyeSBsb3cgcG93ZXIuIAoKV2UgY2FuIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IGJ5IHBsb3R0aW5nIHRoZSB2YWx1ZXMgb2YgdGhlIG5lZ2F0aXZlIGFmZmVjdCBmb3IgdGhlIGdyb3VwcyAoYWxvbmUgYW5kIG5vdCBhbG9uZSkgYXMgd2VsbCBhcyB0aGUgbWVhbiB2YWx1ZXMgZm9yIGVhY2ggZ3JvdXAuCgpgYGB7ciBwbG90IGFuYWx5c2lzIDEgY2FsY3VsYXRpb24sIHdhcm5pbmcgPSBGQUxTRX0KZGF0YV9hbmFseXNpc18xJGFsb25lIDwtIGFzLmZhY3RvcihkYXRhX2FuYWx5c2lzXzEkYWxvbmUpCmxldmVscyhkYXRhX2FuYWx5c2lzXzEkYWxvbmUpIDwtIGMoIk5vdCBBbG9uZSIsICJBbG9uZSIpCgpkYXRhX2FuYWx5c2lzXzEgfD4gCiAgZ2dwbG90KGFlcyh4ID0gYWxvbmUsIHkgPSBuZWdhdGl2ZV9hZmZlY3QpKSArCiAgZ2VvbV9qaXR0ZXIoCiAgICB3aWR0aCA9IDAuMDcsIAogICAgaGVpZ2h0ID0gMCwgCiAgICBhbHBoYSA9IDAuMiwgCiAgICBzaXplID0gMiwgCiAgICBjb2xvciA9IG1wYXRoX3BhbGV0dGVbMV0KICApICsKICBzdGF0X3N1bW1hcnkoCiAgICBnZW9tID0gInBvaW50IiwgCiAgICBmdW4gPSBtZWFuLCAKICAgIGNvbG9yID0gbXBhdGhfcGFsZXR0ZVsyXSwgCiAgICBzaXplID0gMi41CiAgKSArIAogIHN0YXRfc3VtbWFyeSgKICAgIG1hcHBpbmcgPSBhZXMoZ3JvdXAgPSAxKSwKICAgIGdlb20gPSAibGluZSIsCiAgICBmdW4gPSBtZWFuLCAKICAgIGNvbG9yID0gbXBhdGhfcGFsZXR0ZVsyXSwgCiAgICBsaW5ld2lkdGggPSAxCiAgKSArCiAgc3RhdF9zdW1tYXJ5KAogICAgZ2VvbSA9ICJlcnJvcmJhciIsCiAgICBmdW4uZGF0YSA9IG1lYW5fc2UsIAogICAgd2lkdGggPSAwLjA0LCAKICAgIGNvbG9yID0gbXBhdGhfcGFsZXR0ZVsyXQogICkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJOZWdhdGl2ZSBBZmZlY3QgYnkgQWxvbmUiLAogICAgeCA9ICJBbG9uZSIsCiAgICB5ID0gIk5lZ2F0aXZlIEFmZmVjdCIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpBbHRlcm5hdGl2ZWx5LCB3ZSBjYW4gdmlzdWFsaXplIHRoaXMgdXNpbmcgdGhlIGBtb2RlbGJhc2VkYCBwYWNrYWdlIHdoaWNoIGlzIHBhcnQgb2YgYGVhc3lzdGF0c2AuCgpgYGB7ciBwbG90IGFuYWx5c2lzIDF9CmxpYnJhcnkobW9kZWxiYXNlZCkKCmVzdGltYXRlX21lYW5zKG1vZGVsXzEsIGJ5ID0gImFsb25lIikgfD4gCiAgcGxvdCgKICAgIHBvaW50ID0gbGlzdChjb2xvciA9IG1wYXRoX3BhbGV0dGVbMV0pLCAjIEN1c3RvbWlzZSBzb21lIG9mIHRoZSBhZXN0aGV0aWNzCiAgICBsaW5lID0gbGlzdChjb2xvciA9IG1wYXRoX3BhbGV0dGVbMl0pLAogICAgcG9pbnRyYW5nZSA9IGxpc3QoY29sb3IgPSBtcGF0aF9wYWxldHRlWzJdKQogICkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicygKICAgIHRpdGxlID0gIk5lZ2F0aXZlIEFmZmVjdCBieSBBbG9uZSIsCiAgICB4ID0gIkFsb25lIiwKICAgIHkgPSAiTmVnYXRpdmUgQWZmZWN0IgogICkKYGBgCgojIyBRdWVzdGlvbiAyOiBJcyB0aGUgZXZlbmluZy1yZXBvcnRlZCBhbnhpZXR5IGxldmVsIHByZWRpY3RlZCB3ZWxsIHdpdGggdGhlIG1lYW4gYW5kIHRoZSBtYXhpbXVtIGFueGlldHkgbGV2ZWw/CgpJbiB0aGlzIHNlY29uZCBhbmFseXNpcywgd2Ugd2FudCB0byBzZWUgd2hldGhlciB0aGUgb3ZlcmFsbCBhbnhpZXR5IGxldmVscyB0aHJvdWdob3V0IHRoZSBkYXkgKGFzIHJlcG9ydGVkIGluIHRoZSBldmVuaW5nIHF1ZXN0aW9ubmFpcmUpIGNhbiBiZSB3ZWxsIHByZWRpY3RlZCBieSB0aGUgYXZlcmFnZSBhbnhpZXR5IGxldmVscyBhbmQgdGhlIG1heGltdW0gYW54aWV0eSBsZXZlbHMgZXhwZXJpZW5jZWQgdGhyb3VnaG91dCB0aGUgZGF5LiBXZSB3aWxsIHVzZSB0aGUgZm9sbG93aW5nIG1vZGVsIGZvcm11bGE6IAoKJCRcdGV4dHthbnhpZXR5IChldmVuaW5nKSB+IG1lYW4oYW54aWV0eSkgKyBtYXgoYW54aWV0eSkgKyAoMSB8IHBhcnRpY2lwYW50KX0kJAoKYGBge3IgYW5hbHlzaXMgMiBtb2RlbH0KbW9kZWxfMiA8LSBsbWVyKAogIGZvcm11bGEgPSBldmVuaW5nX3NsaWRlcl9hbnhpb3VzIH4gbWVhbl9hbnhpb3VzICsgbWF4X2FueGlvdXMgKyAoMSB8IHBhcnRpY2lwYW50KSwgCiAgZGF0YSA9IGRhdGFfYW5hbHlzaXNfMgopCmBgYAoKQmVmb3JlIGxvb2tpbmcgYXQgdGhlIHJlc3VsdHMsIHdlIHNob3VsZCBjaGVjayB0aGF0IG91ciBtaXhlZCBtb2RlbCJzIGFzc3VtcHRpb25zIGFyZSBtZXQ6IAoKKipMaW5lYXJpdHkqKjogdGhlIHBvaW50cyBmYWxsIHJvdWdobHkgYWxvbmcgYSBzdHJhaWdodCBsaW5lLCBzbyB0aGUgbGluZWFyaXR5IGFzc3VtcHRpb24gaXMgbWV0LgoKYGBge3IgbGluZWFyaXR5IGFuYWx5c2lzIDIsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpjaGVja19jb2xsaW5lYXJpdHkobW9kZWxfMikKcGxvdChjaGVja19jb2xsaW5lYXJpdHkobW9kZWxfMikpCmBgYAoKKipIb21vc2NlZGFzdGljaXR5Kio6IHRoZXJlIGlzIG5vIGNsZWFyIHBhdHRlcm4gaW4gdGhlIHJlc2lkdWFscyB2cy4gZml0dGVkIHZhbHVlcyBwbG90LCBzbyB0aGUgaG9tb3NjZWRhc3RpY2l0eSBhc3N1bXB0aW9uIGlzIGFsc28gbWV0LgoKYGBge3IgaG9tb3NjZWRhc3RpY2l0eSBhbmFseXNpcyAyfQpjaGVja19oZXRlcm9zY2VkYXN0aWNpdHkobW9kZWxfMikKcGxvdChjaGVja19oZXRlcm9zY2VkYXN0aWNpdHkobW9kZWxfMikpCmBgYAoKKipOb3JtYWxpdHkqKjogaXQgc2VlbXMgbGlrZSB0aGVyZSBpcyBtb3JlIGRhdGEgbG9jYXRlZCBhdCB0aGUgZXh0cmVtZXMgb2YgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgbGVzcyBkYXRhIGF0IHRoZSBjZW50ZXIuIEhvd2V2ZXIsIHRoZSByZXNpZHVhbHMgYXJlIHJvdWdobHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuCgpgYGB7ciBub3JtYWxpdHkgYW5hbHlzaXMgMn0KY2hlY2tfbm9ybWFsaXR5KG1vZGVsXzIpIApwbG90KGNoZWNrX25vcm1hbGl0eShtb2RlbF8yKSkKYGBgCgpOb3cgdGhhdCB3ZSBoYXZlIGNoZWNrZWQgdGhlIGFzc3VtcHRpb25zLiBXZSBjYW4gbW92ZSBvbiB0byB0aGUgcmVzdWx0czoKCmBgYHtyIHJlc3VsdHMgYW5hbHlzaXMgMn0Kc3VtbWFyeShtb2RlbF8yKQpBbm92YShtb2RlbF8yKQpgYGAKCkJvdGggdGhlIG1lYW4gYW5kIG1heGltdW0gYW54aWV0eSBsZXZlbCBhcmUgc2lnbmlmaWNhbnQgcHJlZGljdG9ycyBvZiB0aGUgZXZlbmluZy1yZXBvcnRlZCBhbnhpZXR5LiBHb2luZyBmdXJ0aGVyLCB3ZSBtYXkgd29uZGVyIGhvdyB3ZWxsIG91ciBtb2RlbCBwcmVkaWN0cyB0aGUgZXZlbmluZy1yZXBvcnRlZCBhbnhpZXR5IGxldmVscy4gRm9yIHRoaXMsIHdlIGNhbiBjb21wdXRlIHRoZSAkUl4yJC4gV2UgY2FuIHVzZSB0aGUgYHJzcWAgcGFja2FnZSwgd2hpY2ggaW1wbGVtZW50cyBhIG1ldGhvZCBmb3IgY2FsY3VsYXRpbmcgaXQgaW4gbWl4ZWQgbW9kZWxzLiAKClRoZSAkUl4yJCBvZiBtaXhlZCBtb2RlbHMgdXN1YWxseSBjb21lcyB1bnBhY2tlZCBpbnRvIHRocmVlIGRpZmZlcmVudCB2YWx1ZXM6IAoKKiBUaGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgYm90aCB0aGUgZml4ZWQgYW5kIHJhbmRvbSBlZmZlY3RzLCBpbiBgJG1vZGVsYCAoaXQgaXMgdGhlIHN1bSBvZiB0aGUgdHdvICRSXjIkIGJlbG93KS4KKiBUaGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgdGhlIGZpeGVkIGVmZmVjdHMsIGluIGAkZml4ZWRgLgoqIFRoZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBieSB0aGUgcmFuZG9tIGVmZmVjdHMsIGluIGAkcmFuZG9tYC4KClNpbmNlIG91ciByYW5kb20gZWZmZWN0cyBhcmUganVzdCB0byBjb250cm9sIGZvciB0aGUgZWZmZWN0IG9mIGRpZmZlcmVudCBwYXJ0aWNpcGFudHMuIFdlIG1haW5seSB3YW50IHRvIGxvb2sgYXQgdGhlICRSXjIkIGluIGAkZml4ZWRgLiAKCmBgYHtyIHIyIGFuYWx5c2lzIDJ9CmxpYnJhcnkocnNxKQoKcnNxLmxtbShtb2RlbF8yKQpgYGAKPCEtLSBXZSBjYW4gc2VlIHRoYXQgdGhhdCB0aGUgZml4ZWQgZWZmZWN0cyBleHBsYWluIGByIHJvdW5kKHJzcS5sbW0obW9kZWxfMikkZml4ZWQgKiAxMDAsIDIpYCUgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBldmVuaW5nLXJlcG9ydGVkIGFueGlldHkgbGV2ZWxzLiAgLS0+CgojIyBBbmFseXNpczogQ29uY2x1c2lvbgpJbiB0aGlzIHNtYWxsIGFuYWx5c2lzIHdlIGFzc2Vzc2VkIHdoZXRoZXIgYmVpbmcgYWxvbmUgaW5mbHVlbmNlZCBuZWdhdGl2ZSBhZmZlY3QsIGFuZCB3aGV0aGVyIHRoZSBtZWFuIGFuZCBtYXhpbXVtIGFueGlldHkgZXhwZXJpZW5jZWQgdGhyb3VnaG91dCB0aGUgZGF5IGNvdWxkIHByZWRpY3QgdGhlIGV2ZW5pbmctcmVwb3J0ZWQgYW54aWV0eSBsZXZlbC4gCgpXZSBtYWlubHkgZm91bmQgdGhhdCBiZWluZyBhbG9uZSBkb2VzIG5vdCBzZWVtIHRvIGxlYWQgdG8gaGlnaGVyIG5lZ2F0aXZlIGFmZmVjdCBsZXZlbHMuIE1heWJlIHRoZSB3YXkgd2UgY2F0ZWdvcml6ZWQgYmVpbmcgYWxvbmUgd2FzIG5vdCBzZW5zaXRpdmUgZW5vdWdoIHRvIGNhcHR1cmUgdGhlIGVmZmVjdC4gRm9yIGV4YW1wbGUsIHdlIGNhdGVnb3JpemVkIHRoZSByZXNwb25zZSBvcHRpb24gb2YgYmVpbmcgd2l0aCAibXVsdGlwbGUgcGVvcGxlIEkgZG9uInQga25vdyIgYXMgKipub3QqKiBiZWluZyBhbG9uZSwgYnV0IGl0IHdvdWxkIGFsc28gbWFrZSBzZW5zZSB0byBjYXRlZ29yaXplIGl0IGFzIGJlaW5nIGFsb25lLgoKSW4gdGhlIHNlY29uZCBhbmFseXNpcywgd2UgZm91bmQgdGhhdCB0aGUgbWVhbiBhbmQgbWF4aW11bSBhbnhpZXR5IGxldmVscyBleHBlcmllbmNlZCB0aHJvdWdob3V0IHRoZSBkYXkgYXJlIHNpZ25pZmljYW50IHByZWRpY3RvcnMgb2YgdGhlIGV2ZW5pbmctcmVwb3J0ZWQgYW54aWV0eSBsZXZlbC4gVGhpcyBzaG93cyB0aGF0IGJvdGggdGhlIGdlbmVyYWwgbGV2ZWwgb2YgYW54aWV0eSwgYXMgd2VsbCBhcyB0aGUgbWFnbml0dWRlIG9mIGFueGlldHkgcGVha3MgYWZmZWN0IHRoZSBhcHByYWlzYWwgb2Ygb3ZlcmFsbCBhbnhpZXR5IHRocm91Z2hvdXQgdGhlIGRheS4gCg==