3) MAIN.R Script
3.1) Package Installation and Importing
Imports external packages of code used by the program.
```r
```r
```r
library(pacman)
pacman::p_load(dplyr,writexl,clock,ggplot2,plotly,reshape2,reshape,stats,tidyverse,ggsci,
openxlsx,tidyselect,stringr,readxl,lubridate,DT,tibble,haven,DescTools,
imputeTS,berryFunctions,clock,gdata,extrafont,ggpmisc,ggsci,ggh4x,viridis,rlist,
zoom,stringi,ggrepel,fabR,devtools,tcltk)
<!-- rnb-source-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
## 3.2) Module Importing
Runs the scripts within the separate module files in the **Module Compendiums** folder.
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYGBgclxuIyBHZXRzIG1hdGNoZXMgYmV0d2VlbiBuZXdfZmlsZW5hbWVzX3RydW5jIGFuZCB0aGUgUmF3IEZpbGUgTmFtZXMgaW4gdGhlIExvZ2dlcl9JbmZvcm1hdGlvbiBmaWxlLlxubmV3X2ZpbGVfaW5mbyA8LSBuZXdfZmlsZV9pbmZvIHw+IFxuICBtdXRhdGUoZmlsZW5hbWVNYXRjaGVzID0gaWZlbHNlKG5ld19maWxlbmFtZXNfdHJ1bmMgJWluJSBsb2dnZXJJbmZvJFJhd19GaWxlX05hbWUsIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19maWxlbmFtZXNfdHJ1bmMsIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BKSlcblxuIyBHZXQgbm9uLW1hdGNoZXNcbm5ld19maWxlX2luZm8gPC0gbmV3X2ZpbGVfaW5mbyB8PiBcbiAgbXV0YXRlKGZpbGVuYW1lTm9NYXRjaCA9IGlmZWxzZSghKG5ld19maWxlbmFtZXNfdHJ1bmMgJWluJSBsb2dnZXJJbmZvJFJhd19GaWxlX05hbWUpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19maWxlbmFtZXNfdHJ1bmMsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEpKVxuXG5cbiMgSWYgbm9uLW1hdGNoZXMgKFxcbmV3X2ZpbGVuYW1lc190cnVuY1xcIHRoYXQgZG9uJ3QgaGF2ZSBhIGNvcnJlc3BvbmRpbmcgXFxSYXdfRmlsZV9OYW1lXFwgaW4gXFxsb2dnZXJJbmZvXFwpIFxuIyB3ZXJlIGZvdW5kLCBub3RpZnkgdXNlciBhbmQgYXNrIHdoZXRoZXIgdG8gY29udGludWUgb3IgcXVpdC5cbiMgSWYgdGhlIHVzZXIgc2VsZWN0cyBcXHlcXCwgbm9uLW1hdGNoZXMgYXJlIHJlbW92ZWQgZnJvbSBib3RoIG5ld19maWxlbmFtZXMgYW5kIG5ld19maWxlbmFtZXNfdHJ1bmNcbmlmKGxlbmd0aChuZXdfZmlsZV9pbmZvJGZpbGVuYW1lTm9NYXRjaCkgPiAwKSB7XG4gIFxuICAjIFRlbGwgdGhlIHVzZXIgdGhlcmUgYXJlIG5vIG1hdGNoZXMgYmV0d2VlbiB0aGUgbmV3IGRhdGEgZmlsZSBuYW1lcyBhbmQgdGhlIFJhd19GaWxlX05hbWUgY29sdW1uIGluIHRoZSBsb2dnZXIgaW5mb3JtYXRpb24gc2hlZXRcbiAgbWVzc2FnZShwYXN0ZShcXFRoZXJlIGFyZVxcLGxlbmd0aChuZXdfZmlsZV9pbmZvJGZpbGVuYW1lTm9NYXRjaCksXG4gICAgICAgICAgICAgICAgXFxuZXcgZGF0YSBmaWxlcyB3aG8ncyBuYW1lcyBkaWQgbm90IG1hdGNoIGFueSBvZiB0aG9zZSBsaXN0ZWQgaW4gdGhlICdSYXdfRmlsZV9OYW1lJyBjb2x1bW4gaW4gdGhlICdMb2dnZXJfSW5mb3JtYXRpb24ueGxzeCc6XFwpKVxuICBwcmludChkYXRhLmZyYW1lKE5vX01hdGNoZXMgPSBuZXdfZmlsZV9pbmZvJGZpbGVuYW1lTm9NYXRjaCkgfD4gZmlsdGVyKCFpcy5uYShOb19NYXRjaGVzKSkpXG4gIFxuICBcbiAgIyBBc2sgdGhlIHVzZXIgaWYgdGhleSB3b3VsZCBsaWtlIHRvIGNvbnRpbnVlIHdpdGggdGhlIHByb2dyYW0gZXZlbiB0aG91Z2ggdGhlcmUgd2VyZSBpbnN0YW5jZXMgb2Ygbm8gZmlsZSBuYW1lIG1hdGNoZXNcbiAgYW5zd2VyX25vbWF0Y2ggPC0gcmVhZGxpbmUoXFxXb3VsZCB5b3UgbGlrZSB0byBjb250aW51ZT9beS9uXVxcKVxuICBcbiAgXG4gICMgSWYgdGhlIHVzZXIncyBhbnN3ZXIgKGFuc3dlcl9ub21hdGNoKSBpcyBcXG5cXCwgc3RvcHMgdGhlIHByb2dyYW1cbiAgaWYoYW5zd2VyX25vbWF0Y2ggPT0gXFxuXFwpIHtcbiAgICBcbiAgICBcbiAgICBzdG9wKFxcUGxlYXNlIG1ha2UgYXBwcm9wcmlhdGUgZmlsZSBuYW1lIGNoYW5nZXMgdG8gdGhlIG5ldyBmaWxlcyBvciBhZGQgbmV3IHJvd3MgdG8gdGhlICdMb2dnZXJfSW5mb3JtYXRpb24ueGxzeCcgc2hlZXQgYmVmb3JlIHJldHJ5aW5nLlxcKVxuICAgIFxuICAgIFxuICAjIElmIHRoZSB1c2VyJ3MgYW5zd2VyIChhbnN3ZXJfbm9tYXRjaCkgaXMgXFx5XFwuLi5cbiAgfSBlbHNlIGlmIChhbnN3ZXJfbm9tYXRjaCA9PSBcXHlcXCkge1xuICAgIFxuICAgIFxuICAgIG1lc3NhZ2UoXFxcXG5cXG5Qcm9ncmFtIHdpbGwgYXR0ZW1wdCB0byByZW5hbWUgbmV3IGZpbGVzIHRvIHNvbWV0aGluZyB0aGF0IGNhbiBiZSB1c2VkLlxcbklmIGl0IGNhbm5vdFxuYGBgXG5gYGAifQ== -->
```r
```r
```r
# Gets matches between new_filenames_trunc and the Raw File Names in the Logger_Information file.
new_file_info <- new_file_info |>
mutate(filenameMatches = ifelse(new_filenames_trunc %in% loggerInfo$Raw_File_Name,
new_filenames_trunc,
NA))
# Get non-matches
new_file_info <- new_file_info |>
mutate(filenameNoMatch = ifelse(!(new_filenames_trunc %in% loggerInfo$Raw_File_Name),
new_filenames_trunc,
NA))
# If non-matches (\new_filenames_trunc\ that don't have a corresponding \Raw_File_Name\ in \loggerInfo\)
# were found, notify user and ask whether to continue or quit.
# If the user selects \y\, non-matches are removed from both new_filenames and new_filenames_trunc
if(length(new_file_info$filenameNoMatch) > 0) {
# Tell the user there are no matches between the new data file names and the Raw_File_Name column in the logger information sheet
message(paste(\There are\,length(new_file_info$filenameNoMatch),
\new data files who's names did not match any of those listed in the 'Raw_File_Name' column in the 'Logger_Information.xlsx':\))
print(data.frame(No_Matches = new_file_info$filenameNoMatch) |> filter(!is.na(No_Matches)))
# Ask the user if they would like to continue with the program even though there were instances of no file name matches
answer_nomatch <- readline(\Would you like to continue?[y/n]\)
# If the user's answer (answer_nomatch) is \n\, stops the program
if(answer_nomatch == \n\) {
stop(\Please make appropriate file name changes to the new files or add new rows to the 'Logger_Information.xlsx' sheet before retrying.\)
# If the user's answer (answer_nomatch) is \y\...
} else if (answer_nomatch == \y\) {
message(\\n\nProgram will attempt to rename new files to something that can be used.\nIf it cannot
<!-- rnb-source-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
## 3.3) Create Session Info File
Creates an table containing meta data about the current R session. This includes the directories of the last combined and new data files that were selected by the user, so when they run the script again, the dialogue windows open to those directories rather than a remote, high level folder somewhere on the computer (basically saves the user a bit of time).
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYGBgclxuIyBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGxvZ2dlckluZm8gdGliYmxlIHRoYXQgb25seSBjb250YWlucyByb3dzIGNvcnJlc3BvbmRpbmcgdG8gZmlsZSBuYW1lIG1hdGNoZXNcbiMgYW5kIGNyZWF0ZSBhIG5ldyBcXEltcG9ydF9QYXRod2F5c1xcIGNvbHVtbi5cbmluZm9ybWF0aW9uIDwtIGxvZ2dlckluZm8gfD4gXG4gIGZpbHRlcihSYXdfRmlsZV9OYW1lICVpbiUgbmV3X2ZpbGVfaW5mbyRuZXdfZmlsZW5hbWVzX3RydW5jKSB8PiBcbiAgbXV0YXRlKEltcG9ydF9QYXRod2F5cyA9IGltcG9ydF9wYXRod2F5cywgLmFmdGVyID0gUmF3X0ZpbGVfTmFtZSlcblxuXG4jSW1wb3J0IGFsbCBjb21iaW5lZCBkYXRhIGFzIHRpYmJsZXMgYW5kIG9yZ2FuaXplIHRoZW0gaW50byBhIGxpc3Rcbm5ld19kYXRhIDwtIGxhcHBseShpbmZvcm1hdGlvbiRJbXBvcnRfUGF0aHdheXMsIGZ1bmN0aW9uKGZpbGVwYXRoKSBSRUFEX05FV19URU1QRVJfRklMRShmaWxlcGF0aCkgKVxubmFtZXMobmV3X2RhdGEpIDwtIG5ld19maWxlX2luZm8kbmV3X2ZpbGVuYW1lc190cnVuY1xuXG5cbiNJbXBvcnQgYWxsIGNvbWJpbmVkIGRhdGEgYXMgdGliYmxlcyBhbmQgb3JnYW5pemUgdGhlbSBpbnRvIGEgbGlzdFxuY29tYl9kYXRhIDwtIGxhcHBseShjb21iX2ZpbGVwYXRocywgZnVuY3Rpb24oeCkgUkVBRF9URU1QRVJfRklMRSh4KSApXG5uYW1lcyhjb21iX2RhdGEpIDwtIGNvbWJfZmlsZW5hbWVzXG5cblxuXG5tZXNzYWdlKFxcXFxuXFxuUHJvZ3Jlc3M6ICA3KSBJbXBvcnRhdGlvbiBjb21wbGV0ZS5cXClcblxuXG5cblxuIyBSZWFycmFuZ2UgdGhlIHJvd3MgaW4gXFxpbmZvcm1hdGlvblxcIHRvIG1hdGNoIHRoZSBvcmRlciBvZiBkYXRhIGZpbGVzIGluIFxcbmV3X2RhdGFcXCBcbiMgKGJlY2F1c2UgZm9yIHNvbWUgcmVhc29uIGl0IGRpZG4ndCBkbyB0aGF0IGF1dG9tYXRpY2FsbHkgZHVyaW5nIGltcG9ydGF0aW9uKVxueSA9IE5VTExcbmlmKGxlbmd0aChuZXdfZGF0YSkgPiAxKSB7XG4gIGZvcihpIGluIG5ld19maWxlX2luZm8kbmV3X2ZpbGVuYW1lc190cnVuYykge1xuICAgIHggPC0gaW5mb3JtYXRpb24gfD4gZmlsdGVyKFJhd19GaWxlX05hbWUgPT0gaSlcbiAgICB5IDwtIGJpbmRfcm93cyh5LHgpXG4gIH1cbiAgaW5mb3JtYXRpb24gPC0geVxuICBybSh4LHkpXG59XG5gYGBcbmBgYFxuYGBgIn0= -->
```r
```r
```r
# Create a subset of the loggerInfo tibble that only contains rows corresponding to file name matches
# and create a new \Import_Pathways\ column.
information <- loggerInfo |>
filter(Raw_File_Name %in% new_file_info$new_filenames_trunc) |>
mutate(Import_Pathways = import_pathways, .after = Raw_File_Name)
#Import all combined data as tibbles and organize them into a list
new_data <- lapply(information$Import_Pathways, function(filepath) READ_NEW_TEMPER_FILE(filepath) )
names(new_data) <- new_file_info$new_filenames_trunc
#Import all combined data as tibbles and organize them into a list
comb_data <- lapply(comb_filepaths, function(x) READ_TEMPER_FILE(x) )
names(comb_data) <- comb_filenames
message(\\n\nProgress: 7) Importation complete.\)
# Rearrange the rows in \information\ to match the order of data files in \new_data\
# (because for some reason it didn't do that automatically during importation)
y = NULL
if(length(new_data) > 1) {
for(i in new_file_info$new_filenames_trunc) {
x <- information |> filter(Raw_File_Name == i)
y <- bind_rows(y,x)
}
information <- y
rm(x,y)
}
3.5) Choose New Data Files
The section pops up file explorer windows where the user navigates to
and selects the new data and compiled data files they want to combine.
The program creates copies of these data files and places them in the
Inputs/New Data and Inputs/Combined
Data subdirectories.
Note — The dialogue window
might pop up behind all the other open windows on your computer!
# Open a windows explorer window to choose the new data files
new_filepaths <- tk_choose.files(last(sesh_info$lastNewDirectory), caption = "Select New Files")
# If there aren't any character strings in "new_filepaths", stops the script and
# tells the user they need to select new data files.
if(length(new_filepaths) == 0) {
message("No new data files were selected. Exiting...")
break
}
# A tibble containing information about the file pathways, file names, extensions,
# roots, whether or not file name matches were found in the logger info sheet, and
# what name, if any, can replace those file names that don't have matches.
new_file_info <- tibble(
new_filepaths = new_filepaths,
new_filepath_roots = sapply(new_filepaths, function(x) {
str_split(x,"/") |>
unlist() |>
head(-1) |>
paste0(collapse = "/")
}) |> paste0("/"),
new_filenames = new_filepaths |> basename(),
)
# Add a "new_filename_extensions" column to "new_file_info"
new_file_info <- new_file_info |>
mutate(
new_filename_extensions = sapply(new_filenames, function(x) {
str_split(x, "\\.") |>
unlist() |>
last()
}),
.after = "new_filepath_roots"
)
# Add a "new_filenames_trunc" column to "new_file_info" that is just the "new_filenames"
# column without the file extensions
new_file_info <- new_file_info |>
mutate(new_filenames_trunc = new_filenames |>
str_replace_all(pattern = ".xlsx|.csv", replacement = ""),
.after = "new_filename_extensions"
)
message("\n\nProgress: 5.1) Progress: New data files chosen.")
## 5.2) SELECTING COMBINED DATA FILES----
# comb_filepaths <- tk_choose.files(paste0(getwd(),"/Outputs"), caption = "Select Combined Files")
comb_filepaths <- tk_choose.files(paste0(ifelse(is.na(last(sesh_info$lastCombDirectory)), getwd(), last(sesh_info$lastCombDirectory)),"/Outputs"), caption = "Select Combined Files")
# If there aren't any character strings in "comb_filepaths", stops the script and
# tells the user they need to select combined data files.
if(length(comb_filepaths) == 0) {
message("No combined data files were selected. Exiting...")
break
}
# Get just the name of the file
comb_filenames <- comb_filepaths |> basename()
# Removes the month and year at the end of the combined file names and renames
# the actual file names to these altered versions.
# (These dates are only for our benefit when we are manually looking through the data base,
# but actually make writing this script a bit more difficult.)
for(i in 1:length(comb_filenames)) {
filename <- comb_filenames[i]
filename_dateremoved <- gsub("_[A-Za-z]+\\d{4}(?=\\.xlsx)", "", filename, perl = TRUE)
comb_filenames[i] <- filename_dateremoved
}
rm(filename,filename_dateremoved,i)
message("\n\nProgress: 5.2) Progress: Combined data files chosen.")
## 5.3) SELECTING OUTPUT DIRECTORY----
# Open windows explorer window to choose the output file
chosen_output_directory <- tk_choose.dir(last(sesh_info$lastOutputDirectory), caption = "Select Output Directory")
if(length(chosen_output_directory) == 0) {
message("No output directory was selected. Exiting...")
break
}
# Update session info
sesh_info <- sesh_info |> add_row(lastNewDirectory = new_filepaths[1] |>
str_split("/") |>
unlist() |>
head(-1) |>
paste0(collapse = "/"),
lastCombDirectory = comb_filepaths[1] |>
str_split("/") |>
unlist() |>
head(-1) |>
paste0(collapse = "/"),
lastOutputDirectory = chosen_output_directory)
message("\n\nProgress: 5.3) Progress: Output directory chosen.")
3.6) Discover New File Name Matches
This section confirms to the user whether or not there matches found
between the new data files that were selected and corresponding rows of
meta data in the imported Logger_Information.xlsx file. If a match
wasn’t found, the user is prompted to answer whether or not to continue
processing the files for which matches were found (new data files
without matches won’t be used for anything). If a match wasn’t found,
the program won’t be able to properly manipulate the data.
# Gets matches between new_filenames_trunc and the Raw File Names in the Logger_Information file.
new_file_info <- new_file_info |>
mutate(filenameMatches = ifelse(new_filenames_trunc %in% loggerInfo$Raw_File_Name,
new_filenames_trunc,
NA))
# Get non-matches
new_file_info <- new_file_info |>
mutate(filenameNoMatch = ifelse(!(new_filenames_trunc %in% loggerInfo$Raw_File_Name),
new_filenames_trunc,
NA))
# If non-matches ("new_filenames_trunc" that don't have a corresponding "Raw_File_Name" in "loggerInfo")
# were found, notify user and ask whether to continue or quit.
# If the user selects "y", non-matches are removed from both new_filenames and new_filenames_trunc
if(length(new_file_info$filenameNoMatch) > 0) {
# Tell the user there are no matches between the new data file names and the Raw_File_Name column in the logger information sheet
message(paste("There are",length(new_file_info$filenameNoMatch),
"new data files who's names did not match any of those listed in the 'Raw_File_Name' column in the 'Logger_Information.xlsx':"))
print(data.frame(No_Matches = new_file_info$filenameNoMatch) |> filter(!is.na(No_Matches)))
# Ask the user if they would like to continue with the program even though there were instances of no file name matches
answer_nomatch <- readline("Would you like to continue?[y/n]")
# If the user's answer (answer_nomatch) is "n", stops the program
if(answer_nomatch == "n") {
stop("Please make appropriate file name changes to the new files or add new rows to the 'Logger_Information.xlsx' sheet before retrying.")
# If the user's answer (answer_nomatch) is "y"...
} else if (answer_nomatch == "y") {
message("\n\nProgram will attempt to rename new files to something that can be used.\nIf it cannot, the file names of new data without matches will not be used for processing\n\n.")
Sys.sleep(5)
new_file_info <- new_file_info |>
mutate(replaced_names = TREAT_VAGRANT_LOGGER_NAMES(filenameNoMatch))
# Replace the relevant "Raw_File_Name" values in "loggerInfo" to their replacement names
y = sapply(filter(new_file_info, !is.na(replaced_names)) |> pull(replaced_names) , str_which, pattern = loggerInfo$Raw_File_Name)
loggerInfo[y, "Raw_File_Name"] <- new_file_info |> filter(!is.na(replaced_names)) |> pull(new_filenames_trunc)
rm(y)
# Create new vector of pathways for use during importing. This includes any "new_pathways"
# associated with existing "filenameMatches" and with "replaced_names".
import_pathways <<- new_file_info |>
filter(!is.na(filenameMatches) | !is.na(replaced_names)) |>
pull(new_filepaths)
message("Non-matched filenames and filenames that couldn't be renamed have been removed from new filenames list...")
Sys.sleep(3)
} else {
stop("Please provide a 'y' or 'n' answer.")
}
}
message("\n\nProgress: 6) Parsing filename matches complete.")
3.7) Import New and Combined Data
Imports the data that the user selected.
# Create a subset of the loggerInfo tibble that only contains rows corresponding to file name matches
# and create a new "Import_Pathways" column.
information <- loggerInfo |>
filter(Raw_File_Name %in% new_file_info$new_filenames_trunc) |>
mutate(Import_Pathways = import_pathways, .after = Raw_File_Name)
#Import all combined data as tibbles and organize them into a list
new_data <- lapply(information$Import_Pathways, function(filepath) READ_NEW_TEMPER_FILE(filepath) )
names(new_data) <- new_file_info$new_filenames_trunc
#Import all combined data as tibbles and organize them into a list
comb_data <- lapply(comb_filepaths, function(x) READ_TEMPER_FILE(x) )
names(comb_data) <- comb_filenames
message("\n\nProgress: 7) Importation complete.")
# Rearrange the rows in "information" to match the order of data files in "new_data"
# (because for some reason it didn't do that automatically during importation)
y = NULL
if(length(new_data) > 1) {
for(i in new_file_info$new_filenames_trunc) {
x <- information |> filter(Raw_File_Name == i)
y <- bind_rows(y,x)
}
information <- y
rm(x,y)
}
3.8) Set User-Controlled Parameters
Prompts the user to enter in the maximum time gap to fill with
interpolated time and temperature values.
crit_gap_hr <- readline("Set time gap threshold for imputation (in hours): ") |> as.numeric()
3.9) Data Cleaning
Removes and outlier points in the time series. Because automated
processes to identify outliers is very complex, this version of the
program currently only removes values > 50ºC and < -50ºC, which
are too low or high to be considered real.
# For each column with temperature data (air,surface,or ground), replaces temperatures
# that are greater than 50C or less than -50C with NA.
for(i in 1:length(new_data)) {
workDat <- new_data[[i]]
# Subsets the names of colums in workDat containing temperature data.
temperCols <- colnames(workDat) |> PPVO(patterns = c("Air","Surface_Ext","Surface_Int","Depth"))
temperCols <- colnames(workDat)[temperCols]
# For each column in "workDat" containing temperature data...
for(ii in temperCols) {
z <- workDat[[ii]]
z[z > 50 | z < -50] <- NA
workDat[[ii]] <- z
}
print(paste0("CLEANING COMPLETE: ",names(new_data)[i]))
}
rm(i)
3.10) Impute Time Gaps
Fills in gaps in the DateTime and temperature data columns that were
either in the data set already or were produced by the cleaning
process.
# HEADS UP!... the datetime columns in the "new_data" tibbles are now in UTC again.
for(i in 1:length(new_data)) {
workDat_name <- names(new_data)[i]
workDat <- new_data[[i]] |>
OPEN_ROWGAPS() |>
DATETIME_IMPUTE() |>
select(!c(hr_diff,Index))
new_data[[workDat_name]] <- workDat
print(paste0("IMPUTATION COMPLETE: ", workDat_name))
}
rm(workDat, workDat_name)
3.11) Append New Data to Combined Data Files
Appends new data tables available for each site to the relevant
combined temperature data tables. For a site or borehole that has tandem
(x2) ground temperature loggers, the script combines the new tandem
ground temperature data together first, and then appends this combined
new data to the old compiled data.
Warning! —- The treatment of
tandem loggers is currently only valid for tandem GROUND TEMPERATURE
loggers!!
# Creates a copy of the list "comb_data" that new versions of the combined data tibbles
# will be assigned to.
new_comb_data <- comb_data
# Get the levels of the "Borehole" column in "information"
boreholes <- information$Borehole |>
as.factor() |>
levels() |>
as.character()
# For each borehole in "information"...
for(b in boreholes) {
# Isolate rows in "information" pertaining to the site
workInfoBorehole <- information |> filter(Borehole == b)
# If the borehole doesn't have tandem ground temperature loggers...
if (sum(!is.na(workInfoBorehole$Tandem_Loggers)) == 0) {
message(paste0("No tandem ground temperature data for ", b, ". Using nominal appending method."))
# For each logger at the borehole...
for(rawname in workInfoBorehole$Raw_File_Name) {
# Isolate the data types collected by that logger
workTypes <- workInfoBorehole |>
filter(Raw_File_Name == rawname) |>
select(Type) |>
unlist() |>
lapply(function(x) strsplit(x, ",")) |>
unlist() |>
unique()
# Isolate the working new data tibble
workNew <- new_data[[rawname]]
# For each working data type...
for(t in workTypes) {
# Subset the new data tibble with respect to the relevant columns to the data type
if(t == "air") {
workNew_subset <- workNew |> select(DateTime, Air)
} else if(t == "surfaceInt") {
workNew_subset <- workNew |> select(DateTime, Surface_Int)
} else if(t == "surfaceExt") {
workNew_subset <- workNew |> select(DateTime, Surface_Ext)
} else if(t == "ground") {
workNew_subset <- workNew |> select(DateTime, contains("Depth"))
}
# Gets the working combined file name
workComb_filename <- workInfoBorehole |>
filter(Raw_File_Name == rawname) |>
select(Combined_File_Name) |>
as.character() |>
lapply(function(x) strsplit(x, ",") |> unlist()) |>
unlist() |>
str_subset(t) |>
paste0(".xlsx")
# Isolates the working combined data tibble
workComb <- comb_data[[workComb_filename]]
# Join the subset new data tibble to the isolated combined tibble
comb_data[[workComb_filename]] <- workComb |> full_join(workNew_subset)
comb_data[[workComb_filename]] <- comb_data[[workComb_filename]] |>
distinct(DateTime, .keep_all = TRUE)
}
}
} else if(sum(!is.na(workInfoBorehole$Tandem_Loggers)) > 0) {
message(paste0("Tandem ground temperature data appending method initiated for ", b, "."))
# The raw file names for loggers containing tandem data
tandem_raw_filenames <- workInfoBorehole |>
filter(!is.na(Tandem_Loggers)) |>
select(Raw_File_Name) |>
unlist()
if(length(tandem_raw_filenames) < 2) {
stop(paste0("Only one tandem file was detected for ", b,". Two tandem files are needed to
finish appending. Please manually append these data instead."))
}
# Create a new temporary tibble by adding the columns of both tandem files together
to_append <- full_join(new_data[[ tandem_raw_filenames[1] ]], new_data[[ tandem_raw_filenames[2] ]])
# Append the temporary tibble "to_append" to the combined ground file
for(i in comb_data) {
if(nrow(i) == 0) {
i[,"DateTime"] = as.POSIXct(i[,"DateTime"])
}
}
appended_ground <- comb_data[[paste0("groundcombined_", b, ".xlsx")]] |>
full_join(y = to_append) |>
select(starts_with("DateTime") | starts_with("Depth"))
comb_data[[paste0("groundcombined_", b, ".xlsx")]] <- appended_ground
comb_data[[paste0("groundcombined_", b, ".xlsx")]] <- comb_data[[paste0("groundcombined_", b, ".xlsx")]] |>
distinct(DateTime, .keep_all = TRUE)
# If a "aircombined" file exists, append the temporary tibble "to_append" to
# the "aircombined" data
if(sum(str_starts(names(comb_data), "air")) > 0) {
appended_air <- comb_data[[paste0("aircombined_", b, ".xlsx")]] |>
full_join(y = to_append) |>
select(starts_with("DateTime") | starts_with("Air"))
comb_data[[paste0("aircombined_", b, ".xlsx")]] <- appended_air
comb_data[[paste0("groundcombined_", b, ".xlsx")]] <- comb_data[[paste0("groundcombined_", b, ".xlsx")]] |>
distinct(DateTime, .keep_all = TRUE)
}
# If a "surfaceInt" file exists, append the temporary tibble "to_append" to
# the "surfaceInt" data
if(sum(str_starts(names(comb_data), "surfaceInt")) > 0) {
appended_surfaceInt <- comb_data[[paste0("surfaceIntcombined_", b, ".xlsx")]] |>
full_join(y = to_append) |>
select(starts_with("DateTime") | starts_with("Surface_Int"))
comb_data[[paste0("surfaceIntcombined_", b, ".xlsx")]] <- appended_surfaceInt
comb_data[[paste0("groundcombined_", b, ".xlsx")]] <- comb_data[[paste0("groundcombined_", b, ".xlsx")]] |>
distinct(DateTime, .keep_all = TRUE)
}
# If a "surfaceExt" file exists, append the temporary tibble "to_append" to
# the "surfaceExt" data
if(sum(str_starts(names(comb_data), "surfaceExt")) > 0) {
appended_surfaceExt <- comb_data[[paste0("surfaceExtcombined_", b, ".xlsx")]] |>
full_join(y = to_append) |>
select(starts_with("DateTime") | starts_with("Surface_Ext"))
comb_data[[paste0("surfaceExtcombined_", b, ".xlsx")]] <- appended_surfaceExt
comb_data[[paste0("groundcombined_", b, ".xlsx")]] <- comb_data[[paste0("groundcombined_", b, ".xlsx")]] |>
distinct(DateTime, .keep_all = TRUE)
}
}
print(paste0("APPENDING COMPLETE: ", b))
}
3.12) Data Exporting
Exports the data to the directory selected by the user using the
dialogue window. The name of the output combined data file DOES NOT
include the month and year of the update and needs to be added manually
by the user. Doing this automatically is an aim for a future update.
for(i in 1:length(comb_data)) {
forexport <- comb_data[[i]]
name <- names(comb_data)[i] |>
strsplit("\\.") |>
unlist() %>%
.[1]
write_xlsx(forexport, paste0(chosen_output_directory,"/", name, ".xlsx"))
print(paste0("EXPORTATION COMPLETE: ", name))
}
LS0tDQp0aXRsZTogIlRFTVBFUkFUVVJFIERBVEEgQ09NQklOSU5HIFBST0dSQU0iDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgd29yZF9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCiMgVGFibGUgb2YgQ29udGVudHMNCg0KPiAtICAgWzEpIFByb2dyYW0gT3ZlcnZpZXddDQo+ICAgICAtICAgWzEuMSkgUiBQcm9qZWN0IEZpbGUgQ29udGVudHNdDQo+IC0gICBbMikgR3VpZGUgQm9va10NCj4gICAgIC0gICBbMi4xKSBXaGF0IGZpbGUgZm9ybWF0cyBjYW4gYmUgdXNlZD9dDQo+ICAgICAtICAgWzIuMikgV2hlcmUgc2hvdWxkIHRoZSBuZXcgYW5kIGNvbWJpbmVkIGZpbGVzIGJlIGJlZm9yZSBydW5uaW5nIHRoZSBzY3JpcHQ/XQ0KPiAgICAgLSAgIFsyLjMpIEhvdyBzaG91bGQgbXkgbmV3IGRhdGEgYW5kIGNvbWJpbmVkIGRhdGEgZmlsZXMgYmUgbmFtZWQgYmVmb3JlIHJ1bm5pbmcgdGhlIHByb2dyYW0/XQ0KPiAgICAgLSAgIFsyLjQpIFdoYXQgaWYgdGhlIHNpdGUgaXMgYnJhbmQgbmV3IGFuZCBkb2VzIG5vdCBoYXZlIGFueSBjb21iaW5lZCBkYXRhIHlldD9dDQo+ICAgICAgICAgLSAgIFtQcmVwYXJpbmcgYSBOZXcgU2l0ZSBmb3IgUHJvZ3JhbSBVc2VdDQo+IC0gICBbMykgTUFJTi5SIFNjcmlwdF0NCj4gICAgIC0gICBbMy4xKSBQYWNrYWdlIEluc3RhbGxhdGlvbiBhbmQgSW1wb3J0aW5nXQ0KPg0KPiAgICAgLSAgIFszLjIpIE1vZHVsZSBJbXBvcnRpbmddDQo+DQo+ICAgICAtICAgWzMuMykgQ3JlYXRlIFNlc3Npb24gSW5mbyBGaWxlXQ0KPg0KPiAgICAgLSAgIFszLjQpIEltcG9ydCBMb2dnZXIgSW5mb3JtYXRpb24gLnhsc3hdDQo+DQo+ICAgICAtICAgWzMuNSkgQ2hvb3NlIE5ldyBEYXRhIEZpbGVzXQ0KPg0KPiAgICAgLSAgIFszLjYpIERpc2NvdmVyIE5ldyBGaWxlIE5hbWUgTWF0Y2hlc10NCj4NCj4gICAgIC0gICBbMy43KSBJbXBvcnQgTmV3IGFuZCBDb21iaW5lZCBEYXRhXQ0KPg0KPiAgICAgLSAgIFszLjgpIFNldCBVc2VyLUNvbnRyb2xsZWQgUGFyYW1ldGVyc10NCj4NCj4gICAgIC0gICBbMy45KSBEYXRhIENsZWFuaW5nXQ0KPg0KPiAgICAgLSAgIFszLjEwKSBJbXB1dGUgVGltZSBHYXBzXQ0KPg0KPiAgICAgLSAgIFszLjExKSBBcHBlbmQgTmV3IERhdGEgdG8gQ29tYmluZWQgRGF0YSBGaWxlc10NCj4NCj4gICAgIC0gICBbMy4xMikgRGF0YSBFeHBvcnRpbmddDQo+IC0gICBbNCkgVXBjb21pbmcgVXBkYXRlc10NCg0KIyAxKSBQcm9ncmFtIE92ZXJ2aWV3DQoNClRoZSBwdXJwb3NlIG9mIHRoaXMgcHJvZ3JhbSBpcyB0byBhcHBlbmQgbmV3IHRlbXBlcmF0dXJlIGZpbGVzIHRvIGV4aXN0aW5nIHRlbXBlcmF0dXJlIHRpbWUgc2VyaWVzIGZpbGVzLiBUaGUgcHJvZ3JhbSBpcyBkZXNpZ25lZCB0byBoYW5kbGUgYSB2YXJpZXR5IG9mIGRpZmZlcmVudCBmaWxlIG5hbWluZyBjb252ZW50aW9ucywgZmlsZSB0eXBlcywgY29sdW1uL3JvdyBmb3JtYXRzLCBkYXRhIHR5cGVzLCBhbmQgY29sdW1uIG5hbWVzLg0KDQojIyAxLjEpIFIgUHJvamVjdCBGaWxlIENvbnRlbnRzDQoNClsqKipJbnB1dHMqKipdey51bmRlcmxpbmV9ICoqKuKAlCoqKiBGb2xkZXIgd2hlcmUgdGhlIHByb2dyYW0gd2lsbCBjcmVhdGUgYW5kIHN0b3JlIGNvcGllcyBvZiB0aGUgbmV3IGRhdGEgZmlsZXMgYW5kIGNvbWJpbmVkIGRhdGEgZmlsZXMgdGhhdCB5b3Ugc2VsZWN0IHdpdGggdGhlIGRpYWxvZ3VlIGJveC4gV29ya2luZyB3aXRoIGNvcGllcyBvZiB0aGUgbmV3IGFuZCBjb21iaW5lZCBkYXRhIHNhZmVndWFyZHMgYWdhaW5zdCBjb3JydXB0aW5nIG9yIGxvb3Npbmcgb2xkZXIgdmVyc2lvbnMgb2YgdGhvc2UgZGF0YSBhcmNoaXZlZCBpbiB0aGUgRGF0YSBCYXNlLg0KDQpbKioqTW9kdWxlIENvbXBlbmRpdW1zKioqXXsudW5kZXJsaW5lfSDigJQgRm9sZGVyIHdoZXJlIGNvbXBpbGF0aW9ucyBvZiBvdGhlciBzY3JpcHRzIHRoYXQgY29udGFpbiBmdW5jdGlvbnMgdXNlZCBpbiB0aGUgTUFJTi5SIHNjcmlwdCBhcmUgaG91c2VkLiBUaGUgY29tcGVuZGl1bSB1c2VkIGJ5IHRoZSBNQUlOLlIgc2NyaXB0IGlzIGxpc3RlZCBhdCB0aGUgc3RhcnQgb2YgdGhlIHNjcmlwdC4gUHJldmlvdXMgY29tcGVuZGl1bXMgd2VyZSB1c2VkIGluIG9sZGVyIHZlcnNpb25zIG9mIE1BSU4uUi4NCg0KWyoqKk9sZCBTY3JpcHRzKioqXXsudW5kZXJsaW5lfSDigJQgRm9sZGVyIHRoYXQncyBqdXN0IGFuIGFyY2hpdmUgb2Ygb2xkIHZlcnNpb25zIG9mIHRoZSBNQUlOLlIgYW5kIG1vZHVsZSBzY3JpcHRzLiBUaGVzZSBzY3JpcHRzIGFyZW4ndCB1c2VkIGZvciBhbnl0aGluZy4NCg0KWyoqKk9wZXJhdGlvbmFsIEZpbGVzKioqXXsudW5kZXJsaW5lfSDigJQgRm9sZGVyIGNvbnRhaW5pbmcgYW55IGZpbGVzIHRoYXQgYXJlIHVzZWQgaW4gYnkgdGhlIE1BSU4uUiBzY3JpcHQgb3IgYW55IG9mIHRoZSBtb2R1bGVzLiBUaGVzZSBpbmNsdWRlIChidXQgbm90IGxpbWl0ZWQgdG8pIHRoZSAueGxzeCBmaWxlcyBjb250YWluaW5nIGFsbCB0aGUgbWV0YWRhdGEgYWJvdXQgZWFjaCBsb2dnZXIgaW4gdGhlIFBHUiBuZXR3b3JrLg0KDQpbKioqT3V0cHV0cyoqKl17LnVuZGVybGluZX0g4oCUIEEgZm9sZGVyIHRoYXQgY2FuIChvcHRpb25hbGx5KSBiZSB1c2VkIGJ5IHRoZSB1c2VyIHRvIG91dHB1dCB0aGUgbmV3IGNvbWJpbmVkIGZpbGVzIHNvIHRoZXkgY2FuIGJlIGNoZWNrZWQgZm9yIGFjY3VyYWN5IGJlZm9yZSBwdXR0aW5nIHRoZW0gaW50byB0aGUgRGF0YSBCYXNlLg0KDQpbKioqTUFJTi5SKioqXXsudW5kZXJsaW5lfSDigJQgVGhlIG1haW4gUiBzY3JpcHQgdGhhdCBhcHBlbmRzIHRoZSBuZXcgZmlsZXMgdG8gdGhlIG9sZCBjb21iaW5lZCBkYXRhIGZpbGVzLg0KDQohW0ZpZ3VyZSAxLl0oT3BlcmF0aW9uYWwlMjBGaWxlcy9STUQlMjBEb2N1bWVudCUyMEltYWdlcy9Qcm9ncmFtJTIwT3ZlcnZpZXcvUiUyMFByb2plY3QlMjBGb2xkZXIucG5nKQ0KDQo8YnI+DQoNCiMgMikgR3VpZGUgQm9vaw0KDQpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYSBzdGVwLWJ5LXN0ZXAgZ3VpZGUgZm9yIHVzZXJzIHRvIHVzZSB0aGUgcHJvZ3JhbS4NCg0KIyMgMi4xKSBXaGF0IGZpbGUgZm9ybWF0cyBjYW4gYmUgdXNlZD8NCg0KVGFibGUgMSBiZWxvdyBzaG93cyB0aGUgZmlsZSBsb2dnZXIvZmlsZSB0eXBlIGNvbWJpbmF0aW9ucyB0aGF0IGFyZSBzdXBwb3J0ZWQgYnkgdGhlIHByb2dyYW0uIFRoZSBzcGVjaWFsIHByZS1mb3JtYXR0aW5nIHJlcXVpcmVtZW50cyBhcmUgYW55IGZvcm1hdHRpbmcgdGhhdCB0aGUgdXNlciBzaG91bGQgZG91YmxlIGNoZWNrIGJlZm9yZSBydW5uaW5nIHRoZSBzY3JpcHQuIEhvd2V2ZXIsIHRoZXkgdXN1YWxseSBhcmUgYWxyZWFkeSBzZXQgdGhpcyB3YXkgYnkgZGVmYXVsdC4gVGhpcyB0YWJsZSBtYXkgbm90IGNvdmVyIGFsbCBvZiB0aGUgcG9zc2libGUgZm9ybWF0aW5ncyB0aGF0IGNvbWUgb2ZmIG9mIHRoZSBzdGF0aW9ucy4gSWYgdGhlcmUgYXJlIGFueSBzZXR0aW5ncyB0aGF0IGFyZSBtaXNzaW5nIGxldCBDYXNleSBrbm93IHNvIGhlIGNhbiB1cGRhdGUgdGhlIHNjcmlwdC4NCg0KKy0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBMb2dnZXIgVHlwZSAgICB8IEZpbGUgVHlwZSB8IFNwZWNpYWwgUHJlLUZvcm1hdHRpbmcgUmVxdWlyZW1lbnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKz09PT09PT09PT09PT09PT0rPT09PT09PT09PT0rPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCBIT0JPICAgICAgICAgICB8IC0gICAueGxzeCB8IC0gICBUaGVyZSBtdXN0IGJlIGEgc2luZ2xlIERhdGVUaW1lIGNvbHVtbiwgbm90IGRpdmlkZWQgaW50byBzZXBhcmF0ZSBEYXRlIGFuZCBUaW1lIGNvbHVtbnMuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBIT0JPIGJsdWV0b290aCB8IC0gICAuY3N2ICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBQZW5kYW50ICAgICAgICB8IC0gICAueGxzeCB8IC0gICBUaGVyZSBtdXN0IGJlIGEgc2luZ2xlIERhdGVUaW1lIGNvbHVtbiwgbm90IGRpdmlkZWQgaW50byBzZXBhcmF0ZSBEYXRlIGFuZCBUaW1lIGNvbHVtbnMuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICB8ICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICB8IC0gICAuY3N2ICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBMT0dSICAgICAgICAgICB8IC0gICAueGxzeCB8IC0gICBPZiB0aGUgNCBmaWxlcyB0aGF0IHRoZSBMT0dSIG91dHB1dHMsIHlvdSBzaG91bGQgdXNlIHRoZSBvbmUgdGhhdCBjb250YWlucyAidGVtcCIgaW4gdGhlIG5hbWUuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICB8ICAgICAgICAgICB8IC0gICBUaGUgY29sdW1ucyBjb250YWluaW5nIGdyb3VuZCB0ZW1wZXJhdHVyZSBkYXRhIG5lZWQgdG8gaGF2ZSAiQ0giIHNvbWV3aGVyZSBpbiB0aGVpciBjb2x1bW4gbmFtZXMgKHRoaXMgc2VlbXMgdG8gYmUgYSBkZWZhdWx0KS4gfA0KfCAgICAgICAgICAgICAgICB8IC0gICAuY3N2ICB8IC0gICBUaGUgZGF0ZXRpbWVzIHNob3VsZCBiZSB0ZXh0IG9yIG51bWVyaWMgdW5peCB2YWx1ZXMuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICB8ICAgICAgICAgICB8ICAgICAtICAgSWYgdGhlIGRhdGV0aW1lcyB2YWx1ZXMgYXJlIHRleHQsIHRoZXkgc2hvdWxkIGJlIGluIG9uZSBvZiB0aGUgZm9sbG93aW5nIGZvcm1hdHM6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICB8ICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICB8ICAgICAgICAgICB8ICAgICAgICAgLSAgIG1kWSBITSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICB8ICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICB8ICAgICAgICAgICB8ICAgICAgICAgLSAgIFltZCBITVMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBNZXQgU3RhdGlvbiAgICB8IC0gICAuZGF0ICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQo6IFRhYmxlIDEuIFR5cGVzIG9mIGZpbGVzLCBieSBsb2dnZXIgdHlwZSwgdGhhdCBjYW4gYmUgcmVhZCBieSB0aGUgcHJvZ3JhbS4NCg0KPGJyPg0KDQojIyAyLjIpIFdoZXJlIHNob3VsZCB0aGUgbmV3IGFuZCBjb21iaW5lZCBmaWxlcyBiZSBiZWZvcmUgcnVubmluZyB0aGUgc2NyaXB0Pw0KDQpUaGUgbmV3IGRhdGEgYW5kIGNvbWJpbmVkIGRhdGEgZmlsZXMgc2hvdWxkIGFscmVhZHkgYmUgaW5zaWRlIHRoZWlyIGFwcHJvcHJpYXRlIFNpdGUgZm9sZGVyIGluIHRoZSBEYXRhIEJhc2UgKEZpZyAyKS4gVGhlIHByb2dyYW0gd2lsbCBtYWtlIGNvcGllcyBvZiB0aGUgc2VsZWN0IG5ldyBhbmQgY29tYmluZWQgZGF0YSBhbmQgcGxhY2UgdGhlbSBpbiB0aGUgSW5wdXRzIGZvbGRlciBvZiB0aGUgUiBQcm9qZWN0IGZvbGRlciAoRmlnLiAxKS4NCg0KIVtGaWd1cmUgWC5YLl0oT3BlcmF0aW9uYWwlMjBGaWxlcy9STUQlMjBEb2N1bWVudCUyMEltYWdlcy9HdWlkZSUyMEJvb2svRGF0YSUyMEJhc2UlMjBPcmdhbml6YXRpb24ucG5nKQ0KDQo8YnI+DQoNCiMjIDIuMykgSG93IHNob3VsZCBteSBuZXcgZGF0YSBhbmQgY29tYmluZWQgZGF0YSBmaWxlcyBiZSBuYW1lZCBiZWZvcmUgcnVubmluZyB0aGUgcHJvZ3JhbT8NCg0KVGhlIG5ldyBmaWxlcyBjYW4ga2VlcCB0aGUgb3JpZ2luYWwgbmFtZXMgdGhhdCB0aGUgbG9nZ2VyIGdhdmUgdGhlbS4gVGFibGUgMiBzdW1tYXJpemVzIHRoZSBwb3NzaWJsZSBmb3JtYXRzIGZvciB0aGUgY29tYmluZWQgZmlsZSBuYW1lczoNCg0KfCBEYXRhIFR5cGUgfCBGb3JtYXQgfCBFeGFtcGxlIHwNCnwtLS0tfC0tLS18LS0tLXwNCnwgQWlyIHwgYWlyY29tYmluZWRcX3tib3JlaG9sZX1cX3ttb250aFllYXJ9IHwgYWlyY29tYmluZWRfQkgxMWFfU2VwdDIwMTUgfA0KfCBJbnRlcm5hbCBTdXJmYWNlIHwgc3VyZmFjZUludGNvbWJpbmVkXF97Ym9yZWhvbGV9XF97bW9udGhZZWFyfSB8IHN1cmZhY2VJbnRjb21iaW5lZF9CSDI0X0FwcjIwMTkgfA0KfCBFeHRlcm5hbCBTdXJmYWNlIHwgc3VyZmFjZUV4dGNvbWJpbmVkXF97Ym9yZWhvbGV9XF97bW9udGhZZWFyfSB8IHN1cmZhY2VFeHRjb21iaW5lZF9CSDA1X0p1bjIwMjEgfA0KfCBHcm91bmQgfCBncm91bmRjb21iaW5lZFxfe2JvcmVob2xlfVxfe21vbnRoWWVhcn0gfCBncm91bmRjb21iaW5lZF9CSDEzX1NlcHQyMDIwIHwNCg0KOiBUYWJsZSAyLiBDb21iaW5lZCBmaWxlIG5hbWUgZm9ybWF0cy4NCg0KVGhlIG1vbnRoIGFuZCB5ZWFyIGF0IHRoZSBlbmQgb2YgdGhlIGNvbWJpbmVkIGZpbGUgbmFtZXMgYXJlIG9ubHkgdG8gaGVscCB1cyBhcyB1c2VycyBrbm93IHdoZW4gdGhlIGxhc3QgdGltZSB0aGF0IGZpbGUgd2FzIHVwZGF0ZWQsIGFuZCBpcyBub3QgcmVxdWlyZWQgZm9yIHRoZSBmdW5jdGlvbmluZyBvZiB0aGUgcHJvZ3JhbS4NCg0KIyMgMi40KSBXaGF0IGlmIHRoZSBzaXRlIGlzIGJyYW5kIG5ldyBhbmQgZG9lcyBub3QgaGF2ZSBhbnkgY29tYmluZWQgZGF0YSB5ZXQ/DQoNClRoZSBjdXJyZW50IHZlcnNpb24gb2YgdGhlIHByb2dyYW0gZG9lcyBub3QgY3JlYXRlIG5ldyBjb21iaW5lZCBmaWxlcy4gWW91IHdpbGwgbmVlZCB0byBtYWtlIG5ldyBlbXB0eSBjb21iaW5lZCBmaWxlcyBmcm9tIHNjcmF0Y2ggYW5kIGFsc28gZmlsbCB0aGVtIGluIHdpdGggdGhlIGRhdGEgdGhlIGZpcnN0IHRpbWUuIFRoZSBuZXh0IHZlcnNpb24gd2lsbCBlaXRoZXIgcHJvbXB0IHRoZSB1c2VyIHdoZXRoZXIgdG8gY3JlYXRlIGEgbmV3IGNvbWJpbmVkIGZpbGUgZm9yIGEgbmV3IHNpdGUgKHNlZSAqU2VjdGlvbiA0KSBVcGNvbWluZyBVcGRhdGVzKiBmb3IgbW9yZSBkZXRhaWxzKS4gRm9yIG5vdywgZm9sbG93aW5nIHRoZSBpbnN0cnVjdGlvbnMgYmVsb3cgdG8gcHJlcGFyZSB0aGUgZmlsZXMgZm9yIGEgbmV3IHNpdGUgc28gdGhleSBjYW4gYmUgdXNlZCB3aXRoIHRoZSBzY3JpcHQgaW4gdGhlIGZ1dHVyZToNCg0KPiAjIyMgUHJlcGFyaW5nIGEgTmV3IFNpdGUgZm9yIFByb2dyYW0gVXNlDQo+DQo+IDEuICAqKipDcmVhdGUgbmV3IC54bHN4IGNvbWJpbmVkIGZpbGVzIGZvciB0aGUgc2l0ZS4qKioNCj4NCj4gICAgIENoZWNrIHdoYXQga2luZHMgb2YgZGF0YSB5b3UgaGF2ZSAoYWlyLCBpbnRlcm5hbCBzdXJmYWNlLCBleHRlcm5hbCBzdXJmYWNlLCBncm91bmQpLiBGb3IgZWFjaCBkYXRhIHR5cGUsIG1ha2UgYSBuZXcgLnhsc3ggZmlsZSB3aXRoIHRoZSBhcHByb3ByaWF0ZSBjb21iaW5lZCBkYXRhIG5hbWUgKHNlZSB0YWJsZSBpbiBTZWN0aW9uIDIuMyB0YWJsZSkuIFRoZSBjb2x1bW4gbmFtZXMgb2YgZWFjaCBvZiB0aGVzZSBuZXcgY29tYmluZWQgZmlsZXMgYXJlIHNob3duIGluIEZpZyAzLiBZb3UgZG9uJ3QgbmVlZCBhbGwgb2YgdGhlbSwgb25seSB0aGUgb25lcyByZWxldmFudCB0byB0aGUgZGF0YSBjb21pbmcgb2ZmIHRoZSBsb2dnZXI6DQo+DQo+ICAgICAhW0ZpZ3VyZSAzLl0oT3BlcmF0aW9uYWwlMjBGaWxlcy9STUQlMjBEb2N1bWVudCUyMEltYWdlcy9HdWlkZSUyMEJvb2svY29tYmluZWQlMjBmaWxlJTIwdGFibGUlMjBmb3JtYXRzLnBuZykNCj4NCj4gICAgIDxicj4NCj4NCj4gICAgIDEuICAqKipVcGRhdGUgdGhlIGFwcHJvcHJpYXRlIExvZ2dlcl9JbmZvcm1hdGlvbi54bHN4IGZpbGUqKioNCj4NCj4gICAgICAgICBUaGUgTG9nZ2VyX0luZm9ybWF0aW9uLnhsc3ggZmlsZXMgY29udGFpbiBhbGwgb2YgdGhlIG1ldGEgZGF0YSBhYm91dCBlYWNoIG9mIHRoZSBsb2dnZXJzIGZvciBhIHBhcnRpY3VsYXIgcmVnaW9uLiBUaGV5IGFyZSBmb3VuZCBpbiB0aGUgUiBwcm9qZWN0IHN1YmRpcmVjdG9yeSAoRmlnIDQpOg0KPg0KPiAgICAgICAgICoqRGF0YSBCYXNlIFw+IFIgXD4gVGVtcGVyYXR1cmUgQ29tYmluaW5nIFByb2dyYW0gXD4gT3BlcmF0aW9uYWwgRmlsZXMqKg0KPg0KPiAgICAgICAgICFbRmlndXJlIDQuXShPcGVyYXRpb25hbCUyMEZpbGVzL1JNRCUyMERvY3VtZW50JTIwSW1hZ2VzL0d1aWRlJTIwQm9vay9VcGRhdGluZyUyMExvZ2dlcl9JbmZvJTIwKDMpLnBuZykNCj4NCj4gICAgICAgICA8YnI+DQo+DQo+ICAgICAgICAgTWFrZSBhIG5ldyByb3cgaW4gdGhlIGFwcHJvcHJpYXRlIC54bHN4IGZpbGUgZm9yIGVhY2ggbmV3IGxvZ2dlci4gVGFibGUgMyBiZWxvdyBzdW1tYXJpemVzIHdoYXQgdG8gZW50ZXIgaW4gdG8gZWFjaCBjb2x1bW4gaW4gYSBsb2dnZXIgaW5mb3JtYXRpb24gc2hlZXQ6DQo+DQo+ICAgICAgICAgKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQo+ICAgICAgICAgfCBDb2x1bW4gICAgICAgICAgICAgfCBUbyBGaWxsIEluICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgKz09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0rDQo+ICAgICAgICAgfCBSYXdfRmlsZV9OYW1lICAgICAgfCAtICAgVGhlIG5hbWUgb2YgdGhlIGZpbGUgdGhhdCBjb21lcyBvZmYgb2YgdGhlIGxvZ2dlci4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgV0lUSE9VVCB0aGUgZmlsZSBleHRlbnNpb24gKGUuZy4gLnhsc3gpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQo+ICAgICAgICAgfCBMb2dnZXIgICAgICAgICAgICAgfCAtICAgVGhlIHR5cGUgb2YgbG9nZ2VyLiBPbmUgb2Y6ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIEhPQk8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIEhPQk9ibHVldG9vdGggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIExPR1IgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIHBlbmRhbnQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIE1ldCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgKE1ldCBiYXNpY2FsbHkgaW1wbGllcyBDUjEwMDB4KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQo+ICAgICAgICAgfCBCb3JlaG9sZSAgICAgICAgICAgfCAtICAgVGhlIHNpbXBsZSBib3JlaG9sZSBkZXNpZ25hdG9yICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQo+ICAgICAgICAgfCBUeXBlICAgICAgICAgICAgICAgfCAtICAgVHlwZSBvZiBkYXRhIG91dHB1dCBieSB0aGUgbG9nZ2VyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgT25lIG9mOiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIGFpciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIHN1cmZhY2VJbnQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIHN1cmZhY2VFeHQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgLSAgIGdyb3VuZCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgSWYgdGhlcmUgYXJlIG11bHRpcGxlIHR5cGVzLCBzZXBhcmF0ZSB0aGUgdHlwZXMgYnkgYSBjb21tYSBhbmQgTk8gc3BhY2VzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQo+ICAgICAgICAgfCBDb21iaW5lZF9GaWxlX05hbWUgfCAtICAgVGhlIG5hbWUgb2YgdGhlIGV4Y2VsIGZpbGUgdGhhdCBjb250YWlucyB0aGUgY29tcGlsZWQgdGltZSBzZXJpZXMgb2YgZGF0YSBmb3IgdGhlIGxvZ2dlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgV0lUSE9VVCB0aGUgZmlsZSBleHRlbnNpb24gKGUuZy4gLnhsc3gpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgKlNlZSAqKlRhYmxlIDIqKiBmb3IgY29tYmluZWQgZmlsZSBuYW1lIGZvcm1hdHMqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQo+ICAgICAgICAgfCBUYW5kZW1fTG9nZ2VycyAgICAgfCAtICAgVGFuZGVtIGxvZ2dlcnMgYXJlIGxvZ2dlcnMgdGhhdCBhcmUgcGFydCBvZiB0aGUgc2FtZSBib3JlaG9sZSBvciBzdGF0aW9uIHRoYXQgYXJlIHJlY29yZGluZyB0aGUgc2FtZSBkYXRhIHR5cGUgKGUuZy4gdHdvIEhPQk8gbG9nZ2VycyByZWNvcmRpbmcgZ3JvdW5kIHRlbXBlcmF0dXJlIGF0IGRpZmZlcmVudCBzZXRzIG9mIGRlcHRoKSB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgSWYgYSBib3JlaG9sZS9zdGF0aW9uIGhhcyB0YW5kZW0gbG9nZ2VycywgRU5URVIgVEhFIERBVEEgVFlQRSAoYWlyLCBzdXJmYWNlSW50LCBzdXJmYWNlRXh0LCBvciBncm91bmQpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgRUxTRSwgTEVBVkUgQkxBTksgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQo+ICAgICAgICAgfCBDb2x1bW5fTmFtZXMgICAgICAgfCAtICAgQSBjb21tYS1kZWxpbmVhdGVkIGxpc3Qgb2YgdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgQ29sdW1uIG5hbWVzIGFyZSBzZXBhcmF0ZWQgYnkgYSBjb21tYSBhbmQgTk8gU1BBQ0VTICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgfCAtICAgKlNlZSBGaWd1cmUgMyBmb3IgY29sdW1uIG5hbWUgZm9ybWF0cyogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQo+ICAgICAgICAgKy0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQo+DQo+ICAgICAgICAgOiBUYWJsZSAzLiBJbmZvcm1hdGlvbiB0byBmaWxsIGludG8gYSBsb2dnZXIgaW5mb3JtYXRpb24gc2hlZXQuDQoNCiMgMykgTUFJTi5SIFNjcmlwdA0KDQojIyAzLjEpIFBhY2thZ2UgSW5zdGFsbGF0aW9uIGFuZCBJbXBvcnRpbmcNCg0KSW1wb3J0cyBleHRlcm5hbCBwYWNrYWdlcyBvZiBjb2RlIHVzZWQgYnkgdGhlIHByb2dyYW0uDQoNCmBgYHtyfQ0KbGlicmFyeShwYWNtYW4pDQpwYWNtYW46OnBfbG9hZChkcGx5cix3cml0ZXhsLGNsb2NrLGdncGxvdDIscGxvdGx5LHJlc2hhcGUyLHJlc2hhcGUsc3RhdHMsdGlkeXZlcnNlLGdnc2NpLA0KICAgICAgICAgICAgICAgb3Blbnhsc3gsdGlkeXNlbGVjdCxzdHJpbmdyLHJlYWR4bCxsdWJyaWRhdGUsRFQsdGliYmxlLGhhdmVuLERlc2NUb29scywNCiAgICAgICAgICAgICAgIGltcHV0ZVRTLGJlcnJ5RnVuY3Rpb25zLGNsb2NrLGdkYXRhLGV4dHJhZm9udCxnZ3BtaXNjLGdnc2NpLGdnaDR4LHZpcmlkaXMscmxpc3QsDQogICAgICAgICAgICAgICB6b29tLHN0cmluZ2ksZ2dyZXBlbCxmYWJSLGRldnRvb2xzLHRjbHRrKQ0KYGBgDQoNCiMjIDMuMikgTW9kdWxlIEltcG9ydGluZw0KDQpSdW5zIHRoZSBzY3JpcHRzIHdpdGhpbiB0aGUgc2VwYXJhdGUgbW9kdWxlIGZpbGVzIGluIHRoZSAqKk1vZHVsZSBDb21wZW5kaXVtcyoqIGZvbGRlci4NCg0KYGBge3J9DQpzb3VyY2UoIk1vZHVsZSBDb21wZW5kaXVtcy9Db21wZW5kaXVtIDIvQ1VTVE9NIEZVTkNUSU9OUy5SIikNCnNvdXJjZSgiTW9kdWxlIENvbXBlbmRpdW1zL0NvbXBlbmRpdW0gMi9SRUFEX05FV19URU1QRVJfRklMRS5SIikNCmBgYA0KDQojIyAzLjMpIENyZWF0ZSBTZXNzaW9uIEluZm8gRmlsZQ0KDQpDcmVhdGVzIGFuIHRhYmxlIGNvbnRhaW5pbmcgbWV0YSBkYXRhIGFib3V0IHRoZSBjdXJyZW50IFIgc2Vzc2lvbi4gVGhpcyBpbmNsdWRlcyB0aGUgZGlyZWN0b3JpZXMgb2YgdGhlIGxhc3QgY29tYmluZWQgYW5kIG5ldyBkYXRhIGZpbGVzIHRoYXQgd2VyZSBzZWxlY3RlZCBieSB0aGUgdXNlciwgc28gd2hlbiB0aGV5IHJ1biB0aGUgc2NyaXB0IGFnYWluLCB0aGUgZGlhbG9ndWUgd2luZG93cyBvcGVuIHRvIHRob3NlIGRpcmVjdG9yaWVzIHJhdGhlciB0aGFuIGEgcmVtb3RlLCBoaWdoIGxldmVsIGZvbGRlciBzb21ld2hlcmUgb24gdGhlIGNvbXB1dGVyIChiYXNpY2FsbHkgc2F2ZXMgdGhlIHVzZXIgYSBiaXQgb2YgdGltZSkuDQoNCmBgYHtyfQ0KaWYoIWV4aXN0cygic2VzaF9pbmZvIikpIHsNCiAgc2VzaF9pbmZvIDwtIHRpYmJsZShsYXN0TmV3RGlyZWN0b3J5ID0gZ2V0d2QoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0Q29tYkRpcmVjdG9yeSA9IE5BLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RPdXRwdXREaXJlY3RvcnkgPSBOQSkNCn0NCmBgYA0KDQojIyAzLjQpIEltcG9ydCBMb2dnZXIgSW5mb3JtYXRpb24gLnhsc3gNCg0KVGhlIHVzZXIgc2VsZWN0cyB0aGUgcmVnaW9uIHRoYXQgdGhlIG5ldyBsb2dnZXIgZGF0YSBoYXMgY29tZSBmcm9tLiBUaGlzIGltcG9ydHMgdGhlIHJlbGV2YW50IGxvZ2dlciBpbmZvcm1hdGlvbiBleGNlbCBmaWxlIHRoYXQgY29udGFpbnMgbWV0YSBkYXRhIGFib3V0IHRoZSBsb2dnZXJzLiBUaGUgcmVzdCBvZiB0aGUgcHJvZ3JhbSB1c2VzIHRoaXMgbWV0YSBkYXRhIHRvIGRpcmVjdCBob3cgbmV3IGRhdGEgaXMgcmVmb3JtYXR0ZWQgYW5kIGhvdyBuZXcgYW5kIG9sZCBkYXRhIGlzIGNvbWJpbmVkLg0KDQpgYGB7cn0NCiMgVXNlciBzZWxlY3QgcmVnaW9uDQpyZWdpb25zIDwtIGMoIkFsYXNrYSBIaWdod2F5IiwgIkRlbXBzdGVyIEhpZ2h3YXkiLCAiS2xvbmRpa2UgSGlnaHdheSIsIlJlZ2lvbiBOb3QgTGlzdGVkIikNCnJlZ2lvbnNfdGliIDwtIHRpYmJsZShJbmRleCA9IDE6bGVuZ3RoKHJlZ2lvbnMpLCBSZWdpb24gPSBjKCJBbGFza2EgSGlnaHdheSIsICJEZW1wc3RlciBIaWdod2F5IiwgIktsb25kaWtlIEhpZ2h3YXkiLCJSZWdpb24gTm90IExpc3RlZCIpKQ0KcHJpbnQocmVnaW9uc190aWIpDQpzZWxlY3RlZFJlZ2lvbiA8LSByZWFkbGluZSgiU2VsZWN0aW9uIHJlZ2lvbiAoY2hvb3NlIGluZGV4IG51bWJlcik6ICAiKSB8PiBhcy5udW1lcmljKCkNCnNlbGVjdGVkUmVnaW9uIDwtIHJlZ2lvbnNfdGliW1tzZWxlY3RlZFJlZ2lvbiwgMl1dDQoNCiNJbXBvcnQgc2Vuc29yIGRlcHRocyAueGxzeCBzaGVldA0KaWYoc2VsZWN0ZWRSZWdpb24gPT0gIkFsYXNrYSBIaWdod2F5Iikgew0KICBsb2dnZXJJbmZvIDwtIHJlYWRfeGxzeCgiT3BlcmF0aW9uYWwgRmlsZXMvQUhfTG9nZ2VyX0luZm9ybWF0aW9uLnhsc3giKQ0KfSBlbHNlIGlmIChzZWxlY3RlZFJlZ2lvbiA9PSAiRGVtcHN0ZXIgSGlnaHdheSIpIHsNCiAgbG9nZ2VySW5mbyA8LSByZWFkX3hsc3goIk9wZXJhdGlvbmFsIEZpbGVzL0RIX0xvZ2dlcl9JbmZvcm1hdGlvbi54bHN4IikNCn0gZWxzZSBpZiAoc2VsZWN0ZWRSZWdpb24gPT0gIktsb25kaWtlIEhpZ2h3YXkiKSB7DQogIGxvZ2dlckluZm8gPC0gcmVhZF94bHN4KCJPcGVyYXRpb25hbCBGaWxlcy9LSF9Mb2dnZXJfSW5mb3JtYXRpb24ueGxzeCIpDQp9DQpgYGANCg0KIyMgMy41KSBDaG9vc2UgTmV3IERhdGEgRmlsZXMNCg0KVGhlIHNlY3Rpb24gcG9wcyB1cCBmaWxlIGV4cGxvcmVyIHdpbmRvd3Mgd2hlcmUgdGhlIHVzZXIgbmF2aWdhdGVzIHRvIGFuZCBzZWxlY3RzIHRoZSBuZXcgZGF0YSBhbmQgY29tcGlsZWQgZGF0YSBmaWxlcyB0aGV5IHdhbnQgdG8gY29tYmluZS4gVGhlIHByb2dyYW0gY3JlYXRlcyBjb3BpZXMgb2YgdGhlc2UgZGF0YSBmaWxlcyBhbmQgcGxhY2VzIHRoZW0gaW4gdGhlICoqSW5wdXRzL05ldyBEYXRhKiogYW5kICoqSW5wdXRzL0NvbWJpbmVkIERhdGEqKiBzdWJkaXJlY3Rvcmllcy4NCg0KWyoqTm90ZSoqXXsudW5kZXJsaW5lfSAqKuKAlCoqIFRoZSBkaWFsb2d1ZSB3aW5kb3cgbWlnaHQgcG9wIHVwIGJlaGluZCBhbGwgdGhlIG90aGVyIG9wZW4gd2luZG93cyBvbiB5b3VyIGNvbXB1dGVyIQ0KDQpgYGB7cn0NCiMgT3BlbiBhIHdpbmRvd3MgZXhwbG9yZXIgd2luZG93IHRvIGNob29zZSB0aGUgbmV3IGRhdGEgZmlsZXMNCm5ld19maWxlcGF0aHMgPC0gdGtfY2hvb3NlLmZpbGVzKGxhc3Qoc2VzaF9pbmZvJGxhc3ROZXdEaXJlY3RvcnkpLCBjYXB0aW9uID0gIlNlbGVjdCBOZXcgRmlsZXMiKQ0KDQoNCiMgSWYgdGhlcmUgYXJlbid0IGFueSBjaGFyYWN0ZXIgc3RyaW5ncyBpbiAibmV3X2ZpbGVwYXRocyIsIHN0b3BzIHRoZSBzY3JpcHQgYW5kDQojIHRlbGxzIHRoZSB1c2VyIHRoZXkgbmVlZCB0byBzZWxlY3QgbmV3IGRhdGEgZmlsZXMuDQppZihsZW5ndGgobmV3X2ZpbGVwYXRocykgPT0gMCkgew0KICBtZXNzYWdlKCJObyBuZXcgZGF0YSBmaWxlcyB3ZXJlIHNlbGVjdGVkLiBFeGl0aW5nLi4uIikNCiAgYnJlYWsNCn0NCg0KIyBBIHRpYmJsZSBjb250YWluaW5nIGluZm9ybWF0aW9uIGFib3V0IHRoZSBmaWxlIHBhdGh3YXlzLCBmaWxlIG5hbWVzLCBleHRlbnNpb25zLA0KIyByb290cywgd2hldGhlciBvciBub3QgZmlsZSBuYW1lIG1hdGNoZXMgd2VyZSBmb3VuZCBpbiB0aGUgbG9nZ2VyIGluZm8gc2hlZXQsIGFuZA0KIyB3aGF0IG5hbWUsIGlmIGFueSwgY2FuIHJlcGxhY2UgdGhvc2UgZmlsZSBuYW1lcyB0aGF0IGRvbid0IGhhdmUgbWF0Y2hlcy4NCm5ld19maWxlX2luZm8gPC0gdGliYmxlKA0KICANCiAgbmV3X2ZpbGVwYXRocyA9IG5ld19maWxlcGF0aHMsDQogIA0KICBuZXdfZmlsZXBhdGhfcm9vdHMgPSBzYXBwbHkobmV3X2ZpbGVwYXRocywgZnVuY3Rpb24oeCkgew0KICAgIHN0cl9zcGxpdCh4LCIvIikgfD4gDQogICAgICB1bmxpc3QoKSB8PiANCiAgICAgIGhlYWQoLTEpIHw+IA0KICAgICAgcGFzdGUwKGNvbGxhcHNlID0gIi8iKQ0KICB9KSB8PiBwYXN0ZTAoIi8iKSwNCiAgDQogIG5ld19maWxlbmFtZXMgPSBuZXdfZmlsZXBhdGhzIHw+IGJhc2VuYW1lKCksDQogIA0KKQ0KDQojIEFkZCBhICJuZXdfZmlsZW5hbWVfZXh0ZW5zaW9ucyIgY29sdW1uIHRvICJuZXdfZmlsZV9pbmZvIg0KbmV3X2ZpbGVfaW5mbyA8LSBuZXdfZmlsZV9pbmZvIHw+IA0KICBtdXRhdGUoDQogICAgbmV3X2ZpbGVuYW1lX2V4dGVuc2lvbnMgPSBzYXBwbHkobmV3X2ZpbGVuYW1lcywgZnVuY3Rpb24oeCkgew0KICAgICAgc3RyX3NwbGl0KHgsICJcXC4iKSB8PiANCiAgICAgICAgdW5saXN0KCkgfD4gDQogICAgICAgIGxhc3QoKSANCiAgICB9KSwNCiAgICAuYWZ0ZXIgPSAibmV3X2ZpbGVwYXRoX3Jvb3RzIg0KICApDQoNCg0KIyBBZGQgYSAibmV3X2ZpbGVuYW1lc190cnVuYyIgY29sdW1uIHRvICJuZXdfZmlsZV9pbmZvIiB0aGF0IGlzIGp1c3QgdGhlICJuZXdfZmlsZW5hbWVzIg0KIyBjb2x1bW4gd2l0aG91dCB0aGUgZmlsZSBleHRlbnNpb25zDQpuZXdfZmlsZV9pbmZvIDwtIG5ld19maWxlX2luZm8gfD4gDQogIG11dGF0ZShuZXdfZmlsZW5hbWVzX3RydW5jID0gbmV3X2ZpbGVuYW1lcyB8PiANCiAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm4gPSAiLnhsc3h8LmNzdiIsIHJlcGxhY2VtZW50ID0gIiIpLA0KICAgICAgICAgLmFmdGVyID0gIm5ld19maWxlbmFtZV9leHRlbnNpb25zIg0KICAgICAgICAgKQ0KICANCg0KDQptZXNzYWdlKCJcblxuUHJvZ3Jlc3M6ICA1LjEpIFByb2dyZXNzOiAgTmV3IGRhdGEgZmlsZXMgY2hvc2VuLiIpDQoNCg0KDQojIyA1LjIpIFNFTEVDVElORyBDT01CSU5FRCBEQVRBIEZJTEVTLS0tLQ0KDQoNCg0KIyBjb21iX2ZpbGVwYXRocyA8LSB0a19jaG9vc2UuZmlsZXMocGFzdGUwKGdldHdkKCksIi9PdXRwdXRzIiksIGNhcHRpb24gPSAiU2VsZWN0IENvbWJpbmVkIEZpbGVzIikNCmNvbWJfZmlsZXBhdGhzIDwtIHRrX2Nob29zZS5maWxlcyhwYXN0ZTAoaWZlbHNlKGlzLm5hKGxhc3Qoc2VzaF9pbmZvJGxhc3RDb21iRGlyZWN0b3J5KSksIGdldHdkKCksIGxhc3Qoc2VzaF9pbmZvJGxhc3RDb21iRGlyZWN0b3J5KSksIi9PdXRwdXRzIiksIGNhcHRpb24gPSAiU2VsZWN0IENvbWJpbmVkIEZpbGVzIikNCg0KDQojIElmIHRoZXJlIGFyZW4ndCBhbnkgY2hhcmFjdGVyIHN0cmluZ3MgaW4gImNvbWJfZmlsZXBhdGhzIiwgc3RvcHMgdGhlIHNjcmlwdCBhbmQNCiMgdGVsbHMgdGhlIHVzZXIgdGhleSBuZWVkIHRvIHNlbGVjdCBjb21iaW5lZCBkYXRhIGZpbGVzLg0KaWYobGVuZ3RoKGNvbWJfZmlsZXBhdGhzKSA9PSAwKSB7DQogIG1lc3NhZ2UoIk5vIGNvbWJpbmVkIGRhdGEgZmlsZXMgd2VyZSBzZWxlY3RlZC4gRXhpdGluZy4uLiIpDQogIGJyZWFrDQp9DQoNCg0KIyBHZXQganVzdCB0aGUgbmFtZSBvZiB0aGUgZmlsZQ0KY29tYl9maWxlbmFtZXMgPC0gY29tYl9maWxlcGF0aHMgfD4gYmFzZW5hbWUoKQ0KDQoNCiMgUmVtb3ZlcyB0aGUgbW9udGggYW5kIHllYXIgYXQgdGhlIGVuZCBvZiB0aGUgY29tYmluZWQgZmlsZSBuYW1lcyBhbmQgcmVuYW1lcw0KIyB0aGUgYWN0dWFsIGZpbGUgbmFtZXMgdG8gdGhlc2UgYWx0ZXJlZCB2ZXJzaW9ucy4NCiMgKFRoZXNlIGRhdGVzIGFyZSBvbmx5IGZvciBvdXIgYmVuZWZpdCB3aGVuIHdlIGFyZSBtYW51YWxseSBsb29raW5nIHRocm91Z2ggdGhlIGRhdGEgYmFzZSwNCiMgYnV0IGFjdHVhbGx5IG1ha2Ugd3JpdGluZyB0aGlzIHNjcmlwdCBhIGJpdCBtb3JlIGRpZmZpY3VsdC4pDQpmb3IoaSBpbiAxOmxlbmd0aChjb21iX2ZpbGVuYW1lcykpIHsNCiAgDQogIGZpbGVuYW1lIDwtIGNvbWJfZmlsZW5hbWVzW2ldDQogIGZpbGVuYW1lX2RhdGVyZW1vdmVkIDwtIGdzdWIoIl9bQS1aYS16XStcXGR7NH0oPz1cXC54bHN4KSIsICIiLCBmaWxlbmFtZSwgcGVybCA9IFRSVUUpDQogIGNvbWJfZmlsZW5hbWVzW2ldIDwtIGZpbGVuYW1lX2RhdGVyZW1vdmVkDQogIA0KfQ0Kcm0oZmlsZW5hbWUsZmlsZW5hbWVfZGF0ZXJlbW92ZWQsaSkNCg0KDQoNCm1lc3NhZ2UoIlxuXG5Qcm9ncmVzczogIDUuMikgUHJvZ3Jlc3M6ICBDb21iaW5lZCBkYXRhIGZpbGVzIGNob3Nlbi4iKQ0KDQoNCg0KIyMgNS4zKSBTRUxFQ1RJTkcgT1VUUFVUIERJUkVDVE9SWS0tLS0NCg0KDQojIE9wZW4gd2luZG93cyBleHBsb3JlciB3aW5kb3cgdG8gY2hvb3NlIHRoZSBvdXRwdXQgZmlsZQ0KY2hvc2VuX291dHB1dF9kaXJlY3RvcnkgPC0gdGtfY2hvb3NlLmRpcihsYXN0KHNlc2hfaW5mbyRsYXN0T3V0cHV0RGlyZWN0b3J5KSwgY2FwdGlvbiA9ICJTZWxlY3QgT3V0cHV0IERpcmVjdG9yeSIpDQppZihsZW5ndGgoY2hvc2VuX291dHB1dF9kaXJlY3RvcnkpID09IDApIHsNCiAgbWVzc2FnZSgiTm8gb3V0cHV0IGRpcmVjdG9yeSB3YXMgc2VsZWN0ZWQuIEV4aXRpbmcuLi4iKQ0KICBicmVhaw0KfQ0KDQoNCiMgVXBkYXRlIHNlc3Npb24gaW5mbw0Kc2VzaF9pbmZvIDwtIHNlc2hfaW5mbyB8PiBhZGRfcm93KGxhc3ROZXdEaXJlY3RvcnkgPSBuZXdfZmlsZXBhdGhzWzFdIHw+IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX3NwbGl0KCIvIikgfD4gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmxpc3QoKSB8PiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWQoLTEpIHw+IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGNvbGxhcHNlID0gIi8iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0Q29tYkRpcmVjdG9yeSA9IGNvbWJfZmlsZXBhdGhzWzFdIHw+IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX3NwbGl0KCIvIikgfD4gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmxpc3QoKSB8PiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWQoLTEpIHw+IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGNvbGxhcHNlID0gIi8iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0T3V0cHV0RGlyZWN0b3J5ID0gY2hvc2VuX291dHB1dF9kaXJlY3RvcnkpDQoNCg0KDQoNCm1lc3NhZ2UoIlxuXG5Qcm9ncmVzczogIDUuMykgUHJvZ3Jlc3M6ICBPdXRwdXQgZGlyZWN0b3J5IGNob3Nlbi4iKQ0KYGBgDQoNCiMjIDMuNikgRGlzY292ZXIgTmV3IEZpbGUgTmFtZSBNYXRjaGVzDQoNClRoaXMgc2VjdGlvbiBjb25maXJtcyB0byB0aGUgdXNlciB3aGV0aGVyIG9yIG5vdCB0aGVyZSBtYXRjaGVzIGZvdW5kIGJldHdlZW4gdGhlIG5ldyBkYXRhIGZpbGVzIHRoYXQgd2VyZSBzZWxlY3RlZCBhbmQgY29ycmVzcG9uZGluZyByb3dzIG9mIG1ldGEgZGF0YSBpbiB0aGUgaW1wb3J0ZWQgTG9nZ2VyX0luZm9ybWF0aW9uLnhsc3ggZmlsZS4gSWYgYSBtYXRjaCB3YXNuJ3QgZm91bmQsIHRoZSB1c2VyIGlzIHByb21wdGVkIHRvIGFuc3dlciB3aGV0aGVyIG9yIG5vdCB0byBjb250aW51ZSBwcm9jZXNzaW5nIHRoZSBmaWxlcyBmb3Igd2hpY2ggbWF0Y2hlcyB3ZXJlIGZvdW5kIChuZXcgZGF0YSBmaWxlcyB3aXRob3V0IG1hdGNoZXMgd29uJ3QgYmUgdXNlZCBmb3IgYW55dGhpbmcpLiBJZiBhIG1hdGNoIHdhc24ndCBmb3VuZCwgdGhlIHByb2dyYW0gd29uJ3QgYmUgYWJsZSB0byBwcm9wZXJseSBtYW5pcHVsYXRlIHRoZSBkYXRhLg0KDQpgYGB7cn0NCiMgR2V0cyBtYXRjaGVzIGJldHdlZW4gbmV3X2ZpbGVuYW1lc190cnVuYyBhbmQgdGhlIFJhdyBGaWxlIE5hbWVzIGluIHRoZSBMb2dnZXJfSW5mb3JtYXRpb24gZmlsZS4NCm5ld19maWxlX2luZm8gPC0gbmV3X2ZpbGVfaW5mbyB8PiANCiAgbXV0YXRlKGZpbGVuYW1lTWF0Y2hlcyA9IGlmZWxzZShuZXdfZmlsZW5hbWVzX3RydW5jICVpbiUgbG9nZ2VySW5mbyRSYXdfRmlsZV9OYW1lLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfZmlsZW5hbWVzX3RydW5jLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQSkpDQoNCiMgR2V0IG5vbi1tYXRjaGVzDQpuZXdfZmlsZV9pbmZvIDwtIG5ld19maWxlX2luZm8gfD4gDQogIG11dGF0ZShmaWxlbmFtZU5vTWF0Y2ggPSBpZmVsc2UoIShuZXdfZmlsZW5hbWVzX3RydW5jICVpbiUgbG9nZ2VySW5mbyRSYXdfRmlsZV9OYW1lKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfZmlsZW5hbWVzX3RydW5jLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BKSkNCg0KDQojIElmIG5vbi1tYXRjaGVzICgibmV3X2ZpbGVuYW1lc190cnVuYyIgdGhhdCBkb24ndCBoYXZlIGEgY29ycmVzcG9uZGluZyAiUmF3X0ZpbGVfTmFtZSIgaW4gImxvZ2dlckluZm8iKSANCiMgd2VyZSBmb3VuZCwgbm90aWZ5IHVzZXIgYW5kIGFzayB3aGV0aGVyIHRvIGNvbnRpbnVlIG9yIHF1aXQuDQojIElmIHRoZSB1c2VyIHNlbGVjdHMgInkiLCBub24tbWF0Y2hlcyBhcmUgcmVtb3ZlZCBmcm9tIGJvdGggbmV3X2ZpbGVuYW1lcyBhbmQgbmV3X2ZpbGVuYW1lc190cnVuYw0KaWYobGVuZ3RoKG5ld19maWxlX2luZm8kZmlsZW5hbWVOb01hdGNoKSA+IDApIHsNCiAgDQogICMgVGVsbCB0aGUgdXNlciB0aGVyZSBhcmUgbm8gbWF0Y2hlcyBiZXR3ZWVuIHRoZSBuZXcgZGF0YSBmaWxlIG5hbWVzIGFuZCB0aGUgUmF3X0ZpbGVfTmFtZSBjb2x1bW4gaW4gdGhlIGxvZ2dlciBpbmZvcm1hdGlvbiBzaGVldA0KICBtZXNzYWdlKHBhc3RlKCJUaGVyZSBhcmUiLGxlbmd0aChuZXdfZmlsZV9pbmZvJGZpbGVuYW1lTm9NYXRjaCksDQogICAgICAgICAgICAgICAgIm5ldyBkYXRhIGZpbGVzIHdobydzIG5hbWVzIGRpZCBub3QgbWF0Y2ggYW55IG9mIHRob3NlIGxpc3RlZCBpbiB0aGUgJ1Jhd19GaWxlX05hbWUnIGNvbHVtbiBpbiB0aGUgJ0xvZ2dlcl9JbmZvcm1hdGlvbi54bHN4JzoiKSkNCiAgcHJpbnQoZGF0YS5mcmFtZShOb19NYXRjaGVzID0gbmV3X2ZpbGVfaW5mbyRmaWxlbmFtZU5vTWF0Y2gpIHw+IGZpbHRlcighaXMubmEoTm9fTWF0Y2hlcykpKQ0KICANCiAgDQogICMgQXNrIHRoZSB1c2VyIGlmIHRoZXkgd291bGQgbGlrZSB0byBjb250aW51ZSB3aXRoIHRoZSBwcm9ncmFtIGV2ZW4gdGhvdWdoIHRoZXJlIHdlcmUgaW5zdGFuY2VzIG9mIG5vIGZpbGUgbmFtZSBtYXRjaGVzDQogIGFuc3dlcl9ub21hdGNoIDwtIHJlYWRsaW5lKCJXb3VsZCB5b3UgbGlrZSB0byBjb250aW51ZT9beS9uXSIpDQogIA0KICANCiAgIyBJZiB0aGUgdXNlcidzIGFuc3dlciAoYW5zd2VyX25vbWF0Y2gpIGlzICJuIiwgc3RvcHMgdGhlIHByb2dyYW0NCiAgaWYoYW5zd2VyX25vbWF0Y2ggPT0gIm4iKSB7DQogICAgDQogICAgDQogICAgc3RvcCgiUGxlYXNlIG1ha2UgYXBwcm9wcmlhdGUgZmlsZSBuYW1lIGNoYW5nZXMgdG8gdGhlIG5ldyBmaWxlcyBvciBhZGQgbmV3IHJvd3MgdG8gdGhlICdMb2dnZXJfSW5mb3JtYXRpb24ueGxzeCcgc2hlZXQgYmVmb3JlIHJldHJ5aW5nLiIpDQogICAgDQogICAgDQogICMgSWYgdGhlIHVzZXIncyBhbnN3ZXIgKGFuc3dlcl9ub21hdGNoKSBpcyAieSIuLi4NCiAgfSBlbHNlIGlmIChhbnN3ZXJfbm9tYXRjaCA9PSAieSIpIHsNCiAgICANCiAgICANCiAgICBtZXNzYWdlKCJcblxuUHJvZ3JhbSB3aWxsIGF0dGVtcHQgdG8gcmVuYW1lIG5ldyBmaWxlcyB0byBzb21ldGhpbmcgdGhhdCBjYW4gYmUgdXNlZC5cbklmIGl0IGNhbm5vdCwgdGhlIGZpbGUgbmFtZXMgb2YgbmV3IGRhdGEgd2l0aG91dCBtYXRjaGVzIHdpbGwgbm90IGJlIHVzZWQgZm9yIHByb2Nlc3NpbmdcblxuLiIpDQogICAgU3lzLnNsZWVwKDUpDQogICAgDQogICAgDQogICAgbmV3X2ZpbGVfaW5mbyA8LSBuZXdfZmlsZV9pbmZvIHw+IA0KICAgICAgbXV0YXRlKHJlcGxhY2VkX25hbWVzID0gVFJFQVRfVkFHUkFOVF9MT0dHRVJfTkFNRVMoZmlsZW5hbWVOb01hdGNoKSkNCiAgICANCiAgICANCiAgICANCiAgICAjIFJlcGxhY2UgdGhlIHJlbGV2YW50ICJSYXdfRmlsZV9OYW1lIiB2YWx1ZXMgaW4gImxvZ2dlckluZm8iIHRvIHRoZWlyIHJlcGxhY2VtZW50IG5hbWVzDQogICAgeSA9IHNhcHBseShmaWx0ZXIobmV3X2ZpbGVfaW5mbywgIWlzLm5hKHJlcGxhY2VkX25hbWVzKSkgfD4gcHVsbChyZXBsYWNlZF9uYW1lcykgLCBzdHJfd2hpY2gsIHBhdHRlcm4gPSBsb2dnZXJJbmZvJFJhd19GaWxlX05hbWUpDQogICAgbG9nZ2VySW5mb1t5LCAiUmF3X0ZpbGVfTmFtZSJdIDwtIG5ld19maWxlX2luZm8gfD4gZmlsdGVyKCFpcy5uYShyZXBsYWNlZF9uYW1lcykpIHw+IHB1bGwobmV3X2ZpbGVuYW1lc190cnVuYykNCiAgICBybSh5KQ0KICAgIA0KICAgICMgQ3JlYXRlIG5ldyB2ZWN0b3Igb2YgcGF0aHdheXMgZm9yIHVzZSBkdXJpbmcgaW1wb3J0aW5nLiBUaGlzIGluY2x1ZGVzIGFueSAibmV3X3BhdGh3YXlzIg0KICAgICMgYXNzb2NpYXRlZCB3aXRoIGV4aXN0aW5nICJmaWxlbmFtZU1hdGNoZXMiIGFuZCB3aXRoICJyZXBsYWNlZF9uYW1lcyIuDQogICAgaW1wb3J0X3BhdGh3YXlzIDw8LSBuZXdfZmlsZV9pbmZvIHw+IA0KICAgICAgZmlsdGVyKCFpcy5uYShmaWxlbmFtZU1hdGNoZXMpIHwgIWlzLm5hKHJlcGxhY2VkX25hbWVzKSkgfD4gDQogICAgICBwdWxsKG5ld19maWxlcGF0aHMpDQogICAgDQogICAgDQogICAgbWVzc2FnZSgiTm9uLW1hdGNoZWQgZmlsZW5hbWVzIGFuZCBmaWxlbmFtZXMgdGhhdCBjb3VsZG4ndCBiZSByZW5hbWVkIGhhdmUgYmVlbiByZW1vdmVkIGZyb20gbmV3IGZpbGVuYW1lcyBsaXN0Li4uIikNCiAgICBTeXMuc2xlZXAoMykNCiAgICANCiAgICANCiAgfSBlbHNlIHsNCiAgICANCiAgICBzdG9wKCJQbGVhc2UgcHJvdmlkZSBhICd5JyBvciAnbicgYW5zd2VyLiIpDQogICAgDQogIH0NCn0NCg0KDQoNCm1lc3NhZ2UoIlxuXG5Qcm9ncmVzczogIDYpIFBhcnNpbmcgZmlsZW5hbWUgbWF0Y2hlcyBjb21wbGV0ZS4iKQ0KYGBgDQoNCiMjIDMuNykgSW1wb3J0IE5ldyBhbmQgQ29tYmluZWQgRGF0YQ0KDQpJbXBvcnRzIHRoZSBkYXRhIHRoYXQgdGhlIHVzZXIgc2VsZWN0ZWQuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGxvZ2dlckluZm8gdGliYmxlIHRoYXQgb25seSBjb250YWlucyByb3dzIGNvcnJlc3BvbmRpbmcgdG8gZmlsZSBuYW1lIG1hdGNoZXMNCiMgYW5kIGNyZWF0ZSBhIG5ldyAiSW1wb3J0X1BhdGh3YXlzIiBjb2x1bW4uDQppbmZvcm1hdGlvbiA8LSBsb2dnZXJJbmZvIHw+IA0KICBmaWx0ZXIoUmF3X0ZpbGVfTmFtZSAlaW4lIG5ld19maWxlX2luZm8kbmV3X2ZpbGVuYW1lc190cnVuYykgfD4gDQogIG11dGF0ZShJbXBvcnRfUGF0aHdheXMgPSBpbXBvcnRfcGF0aHdheXMsIC5hZnRlciA9IFJhd19GaWxlX05hbWUpDQoNCg0KI0ltcG9ydCBhbGwgY29tYmluZWQgZGF0YSBhcyB0aWJibGVzIGFuZCBvcmdhbml6ZSB0aGVtIGludG8gYSBsaXN0DQpuZXdfZGF0YSA8LSBsYXBwbHkoaW5mb3JtYXRpb24kSW1wb3J0X1BhdGh3YXlzLCBmdW5jdGlvbihmaWxlcGF0aCkgUkVBRF9ORVdfVEVNUEVSX0ZJTEUoZmlsZXBhdGgpICkNCm5hbWVzKG5ld19kYXRhKSA8LSBuZXdfZmlsZV9pbmZvJG5ld19maWxlbmFtZXNfdHJ1bmMNCg0KDQojSW1wb3J0IGFsbCBjb21iaW5lZCBkYXRhIGFzIHRpYmJsZXMgYW5kIG9yZ2FuaXplIHRoZW0gaW50byBhIGxpc3QNCmNvbWJfZGF0YSA8LSBsYXBwbHkoY29tYl9maWxlcGF0aHMsIGZ1bmN0aW9uKHgpIFJFQURfVEVNUEVSX0ZJTEUoeCkgKQ0KbmFtZXMoY29tYl9kYXRhKSA8LSBjb21iX2ZpbGVuYW1lcw0KDQoNCg0KbWVzc2FnZSgiXG5cblByb2dyZXNzOiAgNykgSW1wb3J0YXRpb24gY29tcGxldGUuIikNCg0KDQoNCg0KIyBSZWFycmFuZ2UgdGhlIHJvd3MgaW4gImluZm9ybWF0aW9uIiB0byBtYXRjaCB0aGUgb3JkZXIgb2YgZGF0YSBmaWxlcyBpbiAibmV3X2RhdGEiIA0KIyAoYmVjYXVzZSBmb3Igc29tZSByZWFzb24gaXQgZGlkbid0IGRvIHRoYXQgYXV0b21hdGljYWxseSBkdXJpbmcgaW1wb3J0YXRpb24pDQp5ID0gTlVMTA0KaWYobGVuZ3RoKG5ld19kYXRhKSA+IDEpIHsNCiAgZm9yKGkgaW4gbmV3X2ZpbGVfaW5mbyRuZXdfZmlsZW5hbWVzX3RydW5jKSB7DQogICAgeCA8LSBpbmZvcm1hdGlvbiB8PiBmaWx0ZXIoUmF3X0ZpbGVfTmFtZSA9PSBpKQ0KICAgIHkgPC0gYmluZF9yb3dzKHkseCkNCiAgfQ0KICBpbmZvcm1hdGlvbiA8LSB5DQogIHJtKHgseSkNCn0NCmBgYA0KDQojIyAzLjgpIFNldCBVc2VyLUNvbnRyb2xsZWQgUGFyYW1ldGVycw0KDQpQcm9tcHRzIHRoZSB1c2VyIHRvIGVudGVyIGluIHRoZSBtYXhpbXVtIHRpbWUgZ2FwIHRvIGZpbGwgd2l0aCBpbnRlcnBvbGF0ZWQgdGltZSBhbmQgdGVtcGVyYXR1cmUgdmFsdWVzLg0KDQpgYGB7cn0NCmNyaXRfZ2FwX2hyIDwtIHJlYWRsaW5lKCJTZXQgdGltZSBnYXAgdGhyZXNob2xkIGZvciBpbXB1dGF0aW9uIChpbiBob3Vycyk6ICAiKSB8PiBhcy5udW1lcmljKCkNCmBgYA0KDQojIyAzLjkpIERhdGEgQ2xlYW5pbmcNCg0KUmVtb3ZlcyBhbmQgb3V0bGllciBwb2ludHMgaW4gdGhlIHRpbWUgc2VyaWVzLiBCZWNhdXNlIGF1dG9tYXRlZCBwcm9jZXNzZXMgdG8gaWRlbnRpZnkgb3V0bGllcnMgaXMgdmVyeSBjb21wbGV4LCB0aGlzIHZlcnNpb24gb2YgdGhlIHByb2dyYW0gY3VycmVudGx5IG9ubHkgcmVtb3ZlcyB2YWx1ZXMgXD4gNTDCukMgYW5kIFw8IC01MMK6Qywgd2hpY2ggYXJlIHRvbyBsb3cgb3IgaGlnaCB0byBiZSBjb25zaWRlcmVkIHJlYWwuDQoNCmBgYHtyfQ0KIyBGb3IgZWFjaCBjb2x1bW4gd2l0aCB0ZW1wZXJhdHVyZSBkYXRhIChhaXIsc3VyZmFjZSxvciBncm91bmQpLCByZXBsYWNlcyB0ZW1wZXJhdHVyZXMNCiMgdGhhdCBhcmUgZ3JlYXRlciB0aGFuIDUwQyBvciBsZXNzIHRoYW4gLTUwQyB3aXRoIE5BLg0KDQoNCmZvcihpIGluIDE6bGVuZ3RoKG5ld19kYXRhKSkgew0KDQogd29ya0RhdCA8LSBuZXdfZGF0YVtbaV1dDQoNCiAjIFN1YnNldHMgdGhlIG5hbWVzIG9mIGNvbHVtcyBpbiB3b3JrRGF0IGNvbnRhaW5pbmcgdGVtcGVyYXR1cmUgZGF0YS4NCiB0ZW1wZXJDb2xzIDwtIGNvbG5hbWVzKHdvcmtEYXQpIHw+IFBQVk8ocGF0dGVybnMgPSBjKCJBaXIiLCJTdXJmYWNlX0V4dCIsIlN1cmZhY2VfSW50IiwiRGVwdGgiKSkNCiB0ZW1wZXJDb2xzIDwtIGNvbG5hbWVzKHdvcmtEYXQpW3RlbXBlckNvbHNdDQoNCiAjIEZvciBlYWNoIGNvbHVtbiBpbiAid29ya0RhdCIgY29udGFpbmluZyB0ZW1wZXJhdHVyZSBkYXRhLi4uDQogZm9yKGlpIGluIHRlbXBlckNvbHMpIHsNCg0KICAgeiA8LSB3b3JrRGF0W1tpaV1dDQogICB6W3ogPiA1MCB8IHogPCAtNTBdIDwtIE5BDQogICB3b3JrRGF0W1tpaV1dIDwtIHoNCg0KIH0NCiANCiBwcmludChwYXN0ZTAoIkNMRUFOSU5HIENPTVBMRVRFOiAgIixuYW1lcyhuZXdfZGF0YSlbaV0pKQ0KDQp9DQpybShpKQ0KYGBgDQoNCiMjIDMuMTApIEltcHV0ZSBUaW1lIEdhcHMNCg0KRmlsbHMgaW4gZ2FwcyBpbiB0aGUgRGF0ZVRpbWUgYW5kIHRlbXBlcmF0dXJlIGRhdGEgY29sdW1ucyB0aGF0IHdlcmUgZWl0aGVyIGluIHRoZSBkYXRhIHNldCBhbHJlYWR5IG9yIHdlcmUgcHJvZHVjZWQgYnkgdGhlIGNsZWFuaW5nIHByb2Nlc3MuDQoNCmBgYHtyfQ0KIyBIRUFEUyBVUCEuLi4gdGhlIGRhdGV0aW1lIGNvbHVtbnMgaW4gdGhlICJuZXdfZGF0YSIgdGliYmxlcyBhcmUgbm93IGluIFVUQyBhZ2Fpbi4NCiAgDQpmb3IoaSBpbiAxOmxlbmd0aChuZXdfZGF0YSkpIHsNCiAgd29ya0RhdF9uYW1lIDwtIG5hbWVzKG5ld19kYXRhKVtpXQ0KICB3b3JrRGF0IDwtIG5ld19kYXRhW1tpXV0gfD4NCiAgICBPUEVOX1JPV0dBUFMoKSB8PiANCiAgICBEQVRFVElNRV9JTVBVVEUoKSB8PiANCiAgICBzZWxlY3QoIWMoaHJfZGlmZixJbmRleCkpDQogIG5ld19kYXRhW1t3b3JrRGF0X25hbWVdXSA8LSB3b3JrRGF0DQogIA0KICBwcmludChwYXN0ZTAoIklNUFVUQVRJT04gQ09NUExFVEU6ICAiLCB3b3JrRGF0X25hbWUpKQ0KICANCn0gDQpybSh3b3JrRGF0LCB3b3JrRGF0X25hbWUpDQpgYGANCg0KIyMgMy4xMSkgQXBwZW5kIE5ldyBEYXRhIHRvIENvbWJpbmVkIERhdGEgRmlsZXMNCg0KQXBwZW5kcyBuZXcgZGF0YSB0YWJsZXMgYXZhaWxhYmxlIGZvciBlYWNoIHNpdGUgdG8gdGhlIHJlbGV2YW50IGNvbWJpbmVkIHRlbXBlcmF0dXJlIGRhdGEgdGFibGVzLiBGb3IgYSBzaXRlIG9yIGJvcmVob2xlIHRoYXQgaGFzIHRhbmRlbSAoeDIpIGdyb3VuZCB0ZW1wZXJhdHVyZSBsb2dnZXJzLCB0aGUgc2NyaXB0IGNvbWJpbmVzIHRoZSBuZXcgdGFuZGVtIGdyb3VuZCB0ZW1wZXJhdHVyZSBkYXRhIHRvZ2V0aGVyIGZpcnN0LCBhbmQgdGhlbiBhcHBlbmRzIHRoaXMgY29tYmluZWQgbmV3IGRhdGEgdG8gdGhlIG9sZCBjb21waWxlZCBkYXRhLg0KDQpbKipXYXJuaW5nISoqXXsudW5kZXJsaW5lfSAqKuKAlC0qKiBUaGUgdHJlYXRtZW50IG9mIHRhbmRlbSBsb2dnZXJzIGlzIGN1cnJlbnRseSBvbmx5IHZhbGlkIGZvciB0YW5kZW0gR1JPVU5EIFRFTVBFUkFUVVJFIGxvZ2dlcnMhIQ0KDQpgYGB7cn0NCiMgQ3JlYXRlcyBhIGNvcHkgb2YgdGhlIGxpc3QgImNvbWJfZGF0YSIgdGhhdCBuZXcgdmVyc2lvbnMgb2YgdGhlIGNvbWJpbmVkIGRhdGEgdGliYmxlcw0KIyB3aWxsIGJlIGFzc2lnbmVkIHRvLg0KbmV3X2NvbWJfZGF0YSA8LSBjb21iX2RhdGENCg0KDQoNCiMgR2V0IHRoZSBsZXZlbHMgb2YgdGhlICJCb3JlaG9sZSIgY29sdW1uIGluICJpbmZvcm1hdGlvbiINCmJvcmVob2xlcyA8LSBpbmZvcm1hdGlvbiRCb3JlaG9sZSB8PiANCiAgYXMuZmFjdG9yKCkgfD4gDQogIGxldmVscygpIHw+IA0KICBhcy5jaGFyYWN0ZXIoKQ0KDQoNCiMgRm9yIGVhY2ggYm9yZWhvbGUgaW4gImluZm9ybWF0aW9uIi4uLg0KZm9yKGIgaW4gYm9yZWhvbGVzKSB7DQogIA0KICANCiAgIyBJc29sYXRlIHJvd3MgaW4gImluZm9ybWF0aW9uIiBwZXJ0YWluaW5nIHRvIHRoZSBzaXRlDQogIHdvcmtJbmZvQm9yZWhvbGUgPC0gaW5mb3JtYXRpb24gfD4gZmlsdGVyKEJvcmVob2xlID09IGIpDQogIA0KICANCiAgIyBJZiB0aGUgYm9yZWhvbGUgZG9lc24ndCBoYXZlIHRhbmRlbSBncm91bmQgdGVtcGVyYXR1cmUgbG9nZ2Vycy4uLg0KICBpZiAoc3VtKCFpcy5uYSh3b3JrSW5mb0JvcmVob2xlJFRhbmRlbV9Mb2dnZXJzKSkgPT0gMCkgew0KICAgIA0KICAgIA0KICAgIG1lc3NhZ2UocGFzdGUwKCJObyB0YW5kZW0gZ3JvdW5kIHRlbXBlcmF0dXJlIGRhdGEgZm9yICIsIGIsICIuIFVzaW5nIG5vbWluYWwgYXBwZW5kaW5nIG1ldGhvZC4iKSkNCiAgICANCiAgICANCiAgICAjIEZvciBlYWNoIGxvZ2dlciBhdCB0aGUgYm9yZWhvbGUuLi4NCiAgICBmb3IocmF3bmFtZSBpbiB3b3JrSW5mb0JvcmVob2xlJFJhd19GaWxlX05hbWUpIHsNCiAgICAgIA0KICAgICAgDQogICAgICAjIElzb2xhdGUgdGhlIGRhdGEgdHlwZXMgY29sbGVjdGVkIGJ5IHRoYXQgbG9nZ2VyDQogICAgICB3b3JrVHlwZXMgPC0gd29ya0luZm9Cb3JlaG9sZSB8PiANCiAgICAgICAgZmlsdGVyKFJhd19GaWxlX05hbWUgPT0gcmF3bmFtZSkgfD4gDQogICAgICAgIHNlbGVjdChUeXBlKSB8PiANCiAgICAgICAgdW5saXN0KCkgfD4gDQogICAgICAgIGxhcHBseShmdW5jdGlvbih4KSBzdHJzcGxpdCh4LCAiLCIpKSB8PiANCiAgICAgICAgdW5saXN0KCkgfD4gDQogICAgICAgIHVuaXF1ZSgpDQogICAgICANCiAgICAgIA0KICAgICAgIyBJc29sYXRlIHRoZSB3b3JraW5nIG5ldyBkYXRhIHRpYmJsZQ0KICAgICAgd29ya05ldyA8LSBuZXdfZGF0YVtbcmF3bmFtZV1dDQogICAgICANCiAgICAgIA0KICAgICAgIyBGb3IgZWFjaCB3b3JraW5nIGRhdGEgdHlwZS4uLg0KICAgICAgZm9yKHQgaW4gd29ya1R5cGVzKSB7DQogICAgICAgIA0KICAgICAgICANCiAgICAgICAgDQogICAgICAgIA0KICAgICAgICAjIFN1YnNldCB0aGUgbmV3IGRhdGEgdGliYmxlIHdpdGggcmVzcGVjdCB0byB0aGUgcmVsZXZhbnQgY29sdW1ucyB0byB0aGUgZGF0YSB0eXBlDQogICAgICAgIGlmKHQgPT0gImFpciIpIHsNCiAgICAgICAgICB3b3JrTmV3X3N1YnNldCA8LSB3b3JrTmV3IHw+IHNlbGVjdChEYXRlVGltZSwgQWlyKQ0KICAgICAgICB9IGVsc2UgaWYodCA9PSAic3VyZmFjZUludCIpIHsNCiAgICAgICAgICB3b3JrTmV3X3N1YnNldCA8LSB3b3JrTmV3IHw+IHNlbGVjdChEYXRlVGltZSwgU3VyZmFjZV9JbnQpDQogICAgICAgIH0gZWxzZSBpZih0ID09ICJzdXJmYWNlRXh0Iikgew0KICAgICAgICAgIHdvcmtOZXdfc3Vic2V0IDwtIHdvcmtOZXcgfD4gc2VsZWN0KERhdGVUaW1lLCBTdXJmYWNlX0V4dCkNCiAgICAgICAgfSBlbHNlIGlmKHQgPT0gImdyb3VuZCIpIHsNCiAgICAgICAgICB3b3JrTmV3X3N1YnNldCA8LSB3b3JrTmV3IHw+IHNlbGVjdChEYXRlVGltZSwgY29udGFpbnMoIkRlcHRoIikpDQogICAgICAgIH0NCiAgICAgICAgDQogICAgICAgIA0KICAgICAgIA0KICAgICAgICANCiAgICAgICAgIyBHZXRzIHRoZSB3b3JraW5nIGNvbWJpbmVkIGZpbGUgbmFtZQ0KICAgICAgICB3b3JrQ29tYl9maWxlbmFtZSA8LSB3b3JrSW5mb0JvcmVob2xlIHw+IA0KICAgICAgICAgIGZpbHRlcihSYXdfRmlsZV9OYW1lID09IHJhd25hbWUpIHw+IA0KICAgICAgICAgIHNlbGVjdChDb21iaW5lZF9GaWxlX05hbWUpIHw+DQogICAgICAgICAgYXMuY2hhcmFjdGVyKCkgfD4gDQogICAgICAgICAgbGFwcGx5KGZ1bmN0aW9uKHgpIHN0cnNwbGl0KHgsICIsIikgfD4gdW5saXN0KCkpIHw+IA0KICAgICAgICAgIHVubGlzdCgpIHw+IA0KICAgICAgICAgIHN0cl9zdWJzZXQodCkgfD4gDQogICAgICAgICAgcGFzdGUwKCIueGxzeCIpDQogICAgICAgICAgDQogICAgICAgIA0KICAgICAgICANCiAgICAgICAgDQogICAgICAgICMgSXNvbGF0ZXMgdGhlIHdvcmtpbmcgY29tYmluZWQgZGF0YSB0aWJibGUNCiAgICAgICAgd29ya0NvbWIgPC0gY29tYl9kYXRhW1t3b3JrQ29tYl9maWxlbmFtZV1dDQogICAgICAgIA0KICAgICAgICANCiAgICAgICAgDQogICAgICAgIA0KICAgICAgICAjIEpvaW4gdGhlIHN1YnNldCBuZXcgZGF0YSB0aWJibGUgdG8gdGhlIGlzb2xhdGVkIGNvbWJpbmVkIHRpYmJsZQ0KICAgICAgICBjb21iX2RhdGFbW3dvcmtDb21iX2ZpbGVuYW1lXV0gPC0gd29ya0NvbWIgfD4gZnVsbF9qb2luKHdvcmtOZXdfc3Vic2V0KQ0KICAgICAgICBjb21iX2RhdGFbW3dvcmtDb21iX2ZpbGVuYW1lXV0gPC0gY29tYl9kYXRhW1t3b3JrQ29tYl9maWxlbmFtZV1dIHw+DQogICAgICAgICAgZGlzdGluY3QoRGF0ZVRpbWUsIC5rZWVwX2FsbCA9IFRSVUUpDQogICAgICAgIA0KICAgICAgICANCiAgICAgICAgDQogICAgICB9DQogICAgICANCiAgICAgIA0KICAgIH0NCiAgICANCiAgICANCiAgICANCiAgICANCiAgICANCiAgICANCiAgfSBlbHNlIGlmKHN1bSghaXMubmEod29ya0luZm9Cb3JlaG9sZSRUYW5kZW1fTG9nZ2VycykpID4gMCkgew0KICAgIA0KICAgIA0KICAgIG1lc3NhZ2UocGFzdGUwKCJUYW5kZW0gZ3JvdW5kIHRlbXBlcmF0dXJlIGRhdGEgYXBwZW5kaW5nIG1ldGhvZCBpbml0aWF0ZWQgZm9yICIsIGIsICIuIikpDQogICAgDQogICAgDQogICAgIyBUaGUgcmF3IGZpbGUgbmFtZXMgZm9yIGxvZ2dlcnMgY29udGFpbmluZyB0YW5kZW0gZGF0YQ0KICAgIHRhbmRlbV9yYXdfZmlsZW5hbWVzIDwtIHdvcmtJbmZvQm9yZWhvbGUgfD4gDQogICAgICBmaWx0ZXIoIWlzLm5hKFRhbmRlbV9Mb2dnZXJzKSkgfD4gDQogICAgICBzZWxlY3QoUmF3X0ZpbGVfTmFtZSkgfD4gDQogICAgICB1bmxpc3QoKQ0KICAgIA0KICAgIGlmKGxlbmd0aCh0YW5kZW1fcmF3X2ZpbGVuYW1lcykgPCAyKSB7DQogICAgICBzdG9wKHBhc3RlMCgiT25seSBvbmUgdGFuZGVtIGZpbGUgd2FzIGRldGVjdGVkIGZvciAiLCBiLCIuIFR3byB0YW5kZW0gZmlsZXMgYXJlIG5lZWRlZCB0bw0KICAgICAgZmluaXNoIGFwcGVuZGluZy4gUGxlYXNlIG1hbnVhbGx5IGFwcGVuZCB0aGVzZSBkYXRhIGluc3RlYWQuIikpDQogICAgfQ0KICAgIA0KICAgIA0KICAgIA0KICAgICMgQ3JlYXRlIGEgbmV3IHRlbXBvcmFyeSB0aWJibGUgYnkgYWRkaW5nIHRoZSBjb2x1bW5zIG9mIGJvdGggdGFuZGVtIGZpbGVzIHRvZ2V0aGVyDQogICAgdG9fYXBwZW5kIDwtIGZ1bGxfam9pbihuZXdfZGF0YVtbIHRhbmRlbV9yYXdfZmlsZW5hbWVzWzFdIF1dLCBuZXdfZGF0YVtbIHRhbmRlbV9yYXdfZmlsZW5hbWVzWzJdIF1dKQ0KICAgIA0KICAgIA0KICAgIA0KICAgICMgQXBwZW5kIHRoZSB0ZW1wb3JhcnkgdGliYmxlICJ0b19hcHBlbmQiIHRvIHRoZSBjb21iaW5lZCBncm91bmQgZmlsZQ0KICAgIA0KICAgIGZvcihpIGluIGNvbWJfZGF0YSkgew0KICAgICAgaWYobnJvdyhpKSA9PSAwKSB7DQogICAgICAgIGlbLCJEYXRlVGltZSJdID0gYXMuUE9TSVhjdChpWywiRGF0ZVRpbWUiXSkNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgYXBwZW5kZWRfZ3JvdW5kIDwtIGNvbWJfZGF0YVtbcGFzdGUwKCJncm91bmRjb21iaW5lZF8iLCBiLCAiLnhsc3giKV1dIHw+DQogICAgICBmdWxsX2pvaW4oeSA9IHRvX2FwcGVuZCkgfD4gDQogICAgICBzZWxlY3Qoc3RhcnRzX3dpdGgoIkRhdGVUaW1lIikgfCBzdGFydHNfd2l0aCgiRGVwdGgiKSkNCiAgICBjb21iX2RhdGFbW3Bhc3RlMCgiZ3JvdW5kY29tYmluZWRfIiwgYiwgIi54bHN4IildXSA8LSBhcHBlbmRlZF9ncm91bmQNCiAgICBjb21iX2RhdGFbW3Bhc3RlMCgiZ3JvdW5kY29tYmluZWRfIiwgYiwgIi54bHN4IildXSA8LSBjb21iX2RhdGFbW3Bhc3RlMCgiZ3JvdW5kY29tYmluZWRfIiwgYiwgIi54bHN4IildXSB8PiANCiAgICAgIGRpc3RpbmN0KERhdGVUaW1lLCAua2VlcF9hbGwgPSBUUlVFKQ0KICAgIA0KICAgICMgSWYgYSAiYWlyY29tYmluZWQiIGZpbGUgZXhpc3RzLCBhcHBlbmQgdGhlIHRlbXBvcmFyeSB0aWJibGUgInRvX2FwcGVuZCIgdG8gDQogICAgIyB0aGUgImFpcmNvbWJpbmVkIiBkYXRhDQogICAgaWYoc3VtKHN0cl9zdGFydHMobmFtZXMoY29tYl9kYXRhKSwgImFpciIpKSA+IDApIHsNCiAgICAgIGFwcGVuZGVkX2FpciA8LSBjb21iX2RhdGFbW3Bhc3RlMCgiYWlyY29tYmluZWRfIiwgYiwgIi54bHN4IildXSB8PiANCiAgICAgICAgZnVsbF9qb2luKHkgPSB0b19hcHBlbmQpIHw+IA0KICAgICAgICBzZWxlY3Qoc3RhcnRzX3dpdGgoIkRhdGVUaW1lIikgfCBzdGFydHNfd2l0aCgiQWlyIikpIA0KICAgICAgY29tYl9kYXRhW1twYXN0ZTAoImFpcmNvbWJpbmVkXyIsIGIsICIueGxzeCIpXV0gPC0gYXBwZW5kZWRfYWlyDQogICAgICBjb21iX2RhdGFbW3Bhc3RlMCgiZ3JvdW5kY29tYmluZWRfIiwgYiwgIi54bHN4IildXSA8LSBjb21iX2RhdGFbW3Bhc3RlMCgiZ3JvdW5kY29tYmluZWRfIiwgYiwgIi54bHN4IildXSB8PiANCiAgICAgICAgZGlzdGluY3QoRGF0ZVRpbWUsIC5rZWVwX2FsbCA9IFRSVUUpDQogICAgfQ0KICAgIA0KICAgICMgSWYgYSAic3VyZmFjZUludCIgZmlsZSBleGlzdHMsIGFwcGVuZCB0aGUgdGVtcG9yYXJ5IHRpYmJsZSAidG9fYXBwZW5kIiB0byANCiAgICAjIHRoZSAic3VyZmFjZUludCIgZGF0YQ0KICAgIGlmKHN1bShzdHJfc3RhcnRzKG5hbWVzKGNvbWJfZGF0YSksICJzdXJmYWNlSW50IikpID4gMCkgew0KICAgICAgYXBwZW5kZWRfc3VyZmFjZUludCA8LSBjb21iX2RhdGFbW3Bhc3RlMCgic3VyZmFjZUludGNvbWJpbmVkXyIsIGIsICIueGxzeCIpXV0gfD4gDQogICAgICAgIGZ1bGxfam9pbih5ID0gdG9fYXBwZW5kKSB8PiANCiAgICAgICAgc2VsZWN0KHN0YXJ0c193aXRoKCJEYXRlVGltZSIpIHwgc3RhcnRzX3dpdGgoIlN1cmZhY2VfSW50IikpDQogICAgICBjb21iX2RhdGFbW3Bhc3RlMCgic3VyZmFjZUludGNvbWJpbmVkXyIsIGIsICIueGxzeCIpXV0gPC0gYXBwZW5kZWRfc3VyZmFjZUludA0KICAgICAgY29tYl9kYXRhW1twYXN0ZTAoImdyb3VuZGNvbWJpbmVkXyIsIGIsICIueGxzeCIpXV0gPC0gY29tYl9kYXRhW1twYXN0ZTAoImdyb3VuZGNvbWJpbmVkXyIsIGIsICIueGxzeCIpXV0gfD4gDQogICAgICAgIGRpc3RpbmN0KERhdGVUaW1lLCAua2VlcF9hbGwgPSBUUlVFKQ0KICAgIH0NCiAgICANCiAgICAjIElmIGEgInN1cmZhY2VFeHQiIGZpbGUgZXhpc3RzLCBhcHBlbmQgdGhlIHRlbXBvcmFyeSB0aWJibGUgInRvX2FwcGVuZCIgdG8gDQogICAgIyB0aGUgInN1cmZhY2VFeHQiIGRhdGENCiAgICBpZihzdW0oc3RyX3N0YXJ0cyhuYW1lcyhjb21iX2RhdGEpLCAic3VyZmFjZUV4dCIpKSA+IDApIHsNCiAgICAgIGFwcGVuZGVkX3N1cmZhY2VFeHQgPC0gY29tYl9kYXRhW1twYXN0ZTAoInN1cmZhY2VFeHRjb21iaW5lZF8iLCBiLCAiLnhsc3giKV1dIHw+IA0KICAgICAgICBmdWxsX2pvaW4oeSA9IHRvX2FwcGVuZCkgfD4gDQogICAgICAgIHNlbGVjdChzdGFydHNfd2l0aCgiRGF0ZVRpbWUiKSB8IHN0YXJ0c193aXRoKCJTdXJmYWNlX0V4dCIpKQ0KICAgICAgY29tYl9kYXRhW1twYXN0ZTAoInN1cmZhY2VFeHRjb21iaW5lZF8iLCBiLCAiLnhsc3giKV1dIDwtIGFwcGVuZGVkX3N1cmZhY2VFeHQNCiAgICAgIGNvbWJfZGF0YVtbcGFzdGUwKCJncm91bmRjb21iaW5lZF8iLCBiLCAiLnhsc3giKV1dIDwtIGNvbWJfZGF0YVtbcGFzdGUwKCJncm91bmRjb21iaW5lZF8iLCBiLCAiLnhsc3giKV1dIHw+IA0KICAgICAgICBkaXN0aW5jdChEYXRlVGltZSwgLmtlZXBfYWxsID0gVFJVRSkNCiAgICB9DQogICAgDQogICAgDQogICAgDQogICAgDQogICAgDQogICAgDQogIH0NCiAgDQogIHByaW50KHBhc3RlMCgiQVBQRU5ESU5HIENPTVBMRVRFOiAgIiwgYikpDQogIA0KfQ0KYGBgDQoNCiMjIDMuMTIpIERhdGEgRXhwb3J0aW5nDQoNCkV4cG9ydHMgdGhlIGRhdGEgdG8gdGhlIGRpcmVjdG9yeSBzZWxlY3RlZCBieSB0aGUgdXNlciB1c2luZyB0aGUgZGlhbG9ndWUgd2luZG93LiBUaGUgbmFtZSBvZiB0aGUgb3V0cHV0IGNvbWJpbmVkIGRhdGEgZmlsZSBET0VTIE5PVCBpbmNsdWRlIHRoZSBtb250aCBhbmQgeWVhciBvZiB0aGUgdXBkYXRlIGFuZCBuZWVkcyB0byBiZSBhZGRlZCBtYW51YWxseSBieSB0aGUgdXNlci4gRG9pbmcgdGhpcyBhdXRvbWF0aWNhbGx5IGlzIGFuIGFpbSBmb3IgYSBmdXR1cmUgdXBkYXRlLg0KDQpgYGB7cn0NCmZvcihpIGluIDE6bGVuZ3RoKGNvbWJfZGF0YSkpIHsNCiAgDQogIGZvcmV4cG9ydCA8LSBjb21iX2RhdGFbW2ldXQ0KICBuYW1lIDwtIG5hbWVzKGNvbWJfZGF0YSlbaV0gfD4gDQogICAgc3Ryc3BsaXQoIlxcLiIpIHw+IA0KICAgIHVubGlzdCgpICU+JQ0KICAgIC5bMV0NCiAgd3JpdGVfeGxzeChmb3JleHBvcnQsIHBhc3RlMChjaG9zZW5fb3V0cHV0X2RpcmVjdG9yeSwiLyIsIG5hbWUsICIueGxzeCIpKQ0KICANCiAgDQogIHByaW50KHBhc3RlMCgiRVhQT1JUQVRJT04gQ09NUExFVEU6ICAiLCBuYW1lKSkNCg0KfQ0KYGBgDQoNCiMgNCkgVXBjb21pbmcgVXBkYXRlcw0K