1.0 Introduction

This notebook shows the procedure followed to munge the dataset of Orlando police calls from 2009 to 2015, which I used to put together a police calls map.

1.1 Reading the data

I stored the dataset in Google Drive, just click to download the file. You may get a warning that the file is too big for Google to scan for viruses, so it asks you if you want to download it anyway.


Just click where it says “Download anyway”. It’s tricky to get around that warning in R and just read the file from Google Drive to the computer, because it seems Google periodically changes things around. Hence, I think the safest reproducible way is to manually download the file to your computer as described above.

The dataset clocks in at 474 MB, so it takes some time to download. Also, even if the file was stored locally, using the standard read.csv() takes a long time to read in such a mammoth file. Instead, we can use read_csv() from the readr package. On my computer, read_csv() is about 3 times faster than read.csv(). I assume you clicked on the Google Drive link above and downloaded the file locally. Throughout the notebook, I will keep track of how long it takes to do the various operations on my computer.

library(readr)
start <- Sys.time()
police_calls <- read_csv("Calls_For_Service_2009_-_2015.csv")
read_time <- Sys.time() - start
cat("It took", as.numeric(read_time, units = "secs"), "seconds to read the file.")
It took 46.91853 seconds to read the file.


Take a look at the data:

str(police_calls, give.attr = FALSE)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   3051265 obs. of  8 variables:
 $ Incident Number           : chr  "2009-00000001" "2009-00000002" "2009-00000003" "2009-00000004" ...
 $ Incident Date Time        : chr  "01/01/2009 12:01:00 AM" "01/01/2009 12:01:00 AM" "01/01/2009 12:02:00 AM" "01/01/2009 12:02:00 AM" ...
 $ Incident Location         : chr  "S HIAWASSEE RD / CURRIN DR" "MCCRORY PL / EXECUTIVE CENTER DR" "4200 Block of SCHANK CT" "5500 Block of METROWEST BV" ...
 $ Incident Type             : chr  "Non-emergency assistance" "Unknown trouble" "Suspicious person" "General disturbance" ...
 $ Incident Disposition Class: chr  "Service call" "Service call" "Service call" "Service call" ...
 $ Incident Disposition      : chr  "Turned over to another OPD unit" "Canceled by complainant" "No report; duplicate call" "No report; service call" ...
 $ Status                    : chr  "Mapped" "Mapped" "Mapped" "Mapped" ...
 $ Location                  : chr  "(28.5224150600001, -81.4712688539999)" "(28.5590694700001, -81.3422466369999)" "(28.5144196840001, -81.4318869839999)" "(28.5160530300001, -81.4505605599999)" ...


So we have some 3 million observations and 8 columns.

2.0 Munging

Loading some libraries we will be using

library(chron) # for dealing with chronological objects, i.e., dates and times
library(magrittr) # pipe operator %>%


2.1 Renaming columns

Rename columns to remove spaces:

start <- Sys.time()
names(police_calls)[names(police_calls) == 'Incident Number'] <- 'Incident_Number'
names(police_calls)[names(police_calls) == 'Incident Date Time'] <- 'Incident_Date_Time'
names(police_calls)[names(police_calls) == 'Incident Location'] <- 'Incident_Location'
names(police_calls)[names(police_calls) == 'Incident Type'] <- 'Incident_Type'
names(police_calls)[names(police_calls) == 'Incident Disposition Class'] <- 'Incident_Disposition_Class'
names(police_calls)[names(police_calls) == 'Incident Disposition'] <- 'Incident_Disposition'
rename_time <- Sys.time() - start
cat("It took", rename_time, "seconds to rename the columns.")
It took 10.40395 seconds to rename the columns.

2.2 Dates and times

In the application I plan to use the data for, we need the day of the week the incidents took place, i.e., Monday, Tuesday, … The dates function from the chron package converts “mm/dd/yyyy” into a “dates” object, which in turn is converted to a “Date” object by as.Date(). Then weekdays()

start <- Sys.time()
#Get the dates of incidents
police_calls_dates <- substr(police_calls$Incident_Date_Time, 1, 10) %>% dates() %>% as.Date("%m/%d/%y")
## str(police_calls_dates)
##  Date[1:3051265], format: "2009-01-01" "2009-01-01" "2009-01-01" "2009-01-01" "2009-01-01" ...
#Create new column consisting of days of week incidents took place
# weekdays(yyyy-mm-dd) returns the day of the week of that date
police_calls$days <- weekdays(police_calls_dates) # vector of "Wednesday" , "Saturday", ...
weekdays_time <- Sys.time() - start
cat("It took", weekdays_time, "seconds to compute the weekdays from the dates.")
It took 50.64481 seconds to compute the weekdays from the dates.


Now we want to extract the hour in which the events took place. We will be using 24-hour format, so 4 P.M. will be 16.

start <- Sys.time()
#Get the times, in 24-hour format, the incidents took place
#Using strptime() per this S.O. post:
#http://stackoverflow.com/questions/29833538/convert-12-hour-character-time-to-24-hour
police_calls_times <- substr(police_calls$Incident_Date_Time, 12,22) %>% strptime("%I:%M:%S %p") %>% format(format = "%H:%M:%S") %>% times()
## str(police_calls_times, give.attr = FALSE)
## Class 'times'  atomic [1:3051265] 0.000694 0.000694 0.001389 0.001389 0.002083 ...
#Create new column consisting of hour of day incidents took place
police_calls_hours <- hours(police_calls_times)
hours_time <- Sys.time() - start
cat("It took", as.numeric(hours_time, units = "mins"), "minutes to compute the hours from the times.")
It took 1.046832 minutes to compute the hours from the times.

Now we want to use police_calls_hours to compute the time period in which the events took place: “early_morning”, “morning”, “afternoon”, and “evening”.

start <- Sys.time()
#Create a new column consisting of time period incidents took place
# Determine periods as follows:
# Early Morning (before 6 AM): 0, Morning (between 6 AM and 12 PM): 1
# Afternoon (between noon and 6 PM): 2, Evening (between 6 PM and midnight): 3
# Defining a function that does just that
hours_to_periods <- function(hour) {if (hour %/% 6 == 0) "early_morning" else if (hour %/% 6 == 1) "morning" else if (hour %/% 6 == 2) "afternoon" else "evening"}
# Applying the function to the police_calls_hours vector
police_calls$periods <- sapply(police_calls_hours, hours_to_periods)
periods_time <- Sys.time() - start
cat("It took", as.numeric(periods_time, units = "secs"), "seconds to compute the hours from the times.")
It took 17.65679 seconds to compute the hours from the times.

2.3 Circumscribing incidents into categories

I ultimately want to design a UI that allows users to click on the incident categories they want displayed, using checkboxes. The data as given has too many incident types to allow that.

num_types <- length(unique(police_calls$Incident_Type))
cat("There are", num_types, "incident types in the dataset.")
There are 142 incident types in the dataset.


It is not practical to include 142 checkboxes in a UI, so I want to aggregate them into a few categories so that the UI won’t be overwhelming. Here is the list of all the incident types.

types <- rep("placeholder", num_types) #creates vector of "placeholder" repeated num_types times
names(types) <- as.vector(unique(police_calls$Incident_Type)) # adds names to vector just created
# types is a named vector. It's a little like a dictionary in Python. The names of the vector
# are like a dictionary's keys in Python
# https://stackoverflow.com/questions/2858014/working-with-dictionaries-lists-in-r
## types[1:3]
## Non-emergency assistance          Unknown trouble        Suspicious person 
##            "placeholder"            "placeholder"            "placeholder"
## types["Unknown trouble"]
## Unknown trouble 
##   "placeholder"
names(types)
  [1] "Non-emergency assistance"               "Unknown trouble"                       
  [3] "Suspicious person"                      "General disturbance"                   
  [5] "Suspicious incident"                    "Commercial alarm"                      
  [7] "Noise ordinance violation"              "Battery - shooting"                    
  [9] "House/business check"                   "Trespasser"                            
 [11] "Residential alarm"                      "Stopping vehicle"                      
 [13] "Designated Patrol Area - available"     "Threats/assault"                       
 [15] "Door alarm"                             "Drunk driver"                          
 [17] "General investigation"                  "Drunk pedestrian"                      
 [19] "Commercial B&E"                         "Ambulance escort"                      
 [21] "Man down"                               "General disturbance - armed"           
 [23] "Officer with prisoner"                  "Battery"                               
 [25] "Accident (minor)"                       "Direct traffic"                        
 [27] "Illegally parked cars"                  "Vandalism/criminal mischief"           
 [29] "Vehicle B&E"                            "Suspicious vehicle"                    
 [31] "Person robbery"                         "Accident (injuries)"                   
 [33] "Theft"                                  "Signal out"                            
 [35] "Missing person - juvenile"              "Reckless vehicle"                      
 [37] "Accident with road blockage"            "Fugitive from justice - misdemeanor"   
 [39] "Drug violation"                         "Immediate backup"                      
 [41] "Domestic disturbance"                   "Sick or injured person"                
 [43] "Residential B&E"                        "Disabled occupied vehicle"             
 [45] "Fugitive from justice - felony"         "Home Invasion"                         
 [47] "Fraud-counterfeit"                      "Battery - fight in progress"           
 [49] "Mentally-ill person (violent)"          "Weapons/armed"                         
 [51] "911 hang up"                            "Theft by shoplifting"                  
 [53] "Hit and run (minor)"                    "Check the well being of"               
 [55] "Bomb explosion"                         "Fire"                                  
 [57] "Designated Patrol Area"                 "Stolen/lost tag"                       
 [59] "Child neglect"                          "Community Orientated Policing detail"  
 [61] "Stolen vehicle"                         "Lost/found property"                   
 [63] "Solicitor"                              "Bank alarm"                            
 [65] "Other sex crimes"                       "Trash dumping"                         
 [67] "Mentally-ill person (non-violent)"      "Threatening animal"                    
 [69] "Fugitive from justice"                  "Stalking"                              
 [71] "Obstruction on highway"                 "Recovered stolen vehicle"              
 [73] "Car jacking"                            "Attempted suicide"                     
 [75] "Abandoned vehicle"                      "Commercial robbery"                    
 [77] "Rape"                                   "Recovered missing person"              
 [79] "Missing person - adult"                 "Misdemeanor investigation"             
 [81] "Open door/window investigation"         "Arson fire"                            
 [83] "Hold-up alarm"                          "Bad check passed"                      
 [85] "Obscene/harassing phone calls"          "Hit and run with road blockage"        
 [87] "Dead person"                            "Nuisance animal"                       
 [89] "Liquor law violation"                   "Prostitution"                          
 [91] "Battery - cutting"                      "Special traffic detail"                
 [93] "Threats/assaults - armed"               "Impersonating Police Officer"          
 [95] "Hit and run (injuries)"                 "Felony investigation"                  
 [97] "Accident (general disturbance)"         "Deviate sexual activities"             
 [99] "Child abuse"                            "Vehicle alarm"                         
[101] "Prowler"                                "School zone crossing"                  
[103] "Trespass authorization"                 "Recovered stolen/lost tag"             
[105] "Suspicious luggage"                     "Suicide"                               
[107] "Bomb threat"                            "Red light out"                         
[109] "Illegal fishing"                        "Rush-Officer needs help"               
[111] "Security checkpoint alarm"              "Trespass warning"                      
[113] "Battery - weapon present"               "Strong-arm robbery"                    
[115] "Law Enforcement Officer escort"         "Gambling"                              
[117] "Suspicious boat"                        "Hitchhiker"                            
[119] "Abandoned boat"                         "Kidnapping"                            
[121] "Industrial accident"                    "Suspicious video"                      
[123] "Dead animal"                            "Money transfer"                        
[125] "Suspicious car/occupant armed"          "Bank robbery"                          
[127] "Escaped prisoner"                       "Designated Patrol Area - intersections"
[129] "Drug violation - armed"                 "Drowning"                              
[131] "Attempted rape"                         "Reckless boat"                         
[133] "Stable duty"                            "Boating accident"                      
[135] "Airplane accident"                      "Murder"                                
[137] "Bribery"                                "Trespasser - drunk"                    
[139] "Fugitive from justice - armed"          "Kidnapping - armed"                    
[141] "Disturbance involving tow truck/driver" "K-9 requested"                         


We will put them into some general categories. For example, “Suspicious person” (#3), “Suspicious incident”(#5), “Suspicious vehicle”(#30), and others similar could be put into a single bin called “suspicious”. Likewise, “General disturbance”(#4), “General disturbance - armed”(#22), and “Domestic disturbance”(#41) could all be filed under “disturbance”.

# We are changing the values of these "keys" (the names in the named vector) to be our super-categories, i.e., "suspicious", "disturbance", "etc."
types[c(1:2, 7, 9, 13, 17:18, 20:21, 23, 35, 40, 42, 49:51, 54:57, 59:60, 62:63, 66:68, 70, 78:82, 84:85, 87:90, 94, 96, 99, 105, 107, 109:110, 115:116, 120:121, 123:124, 127:128, 130, 133, 137, 140, 142)] = "other"
types[c(3, 5, 30, 101, 105, 117:118, 122, 125)] = "suspicious"
types[c(4, 22, 41)] = "disturbance"
types[c(6, 11, 15, 64, 83, 100, 111)] = "alarm"
types[c(8, 24, 48, 91, 113)] = "battery"
types[c(10, 103, 112, 138)] = "trespass"
types[c(12, 20, 25:27, 32, 34, 36:37, 44, 53, 71, 75, 86, 92, 95, 97, 102, 108, 119, 132, 134:135, 141)] = "traffic"
types[c(14, 93)] = "assault"
types[c(16)] = "DUI"
types[c(19, 29, 43, 46)] = "B&E"
types[c(28)] = "mischief"
types[c(31, 76, 114, 126)] = "robbery"
types[c(33, 52, 58, 61, 72:73, 104)] = "theft"
types[c(38, 45, 69, 139)] = "fugitive"
types[c(39, 129)] = "drugs"
types[c(47 )] = "fraud"
types[c(63, 65, 77, 90, 98, 131)] = "sex crimes"
types[c(74, 106)] = "suicide"
types[c(136)] = "murder"
## types[1:3]
## Non-emergency assistance          Unknown trouble        Suspicious person 
##                  "other"                  "other"             "suspicious" 


Finally, we can create the categories column and add it to the police_calls dataframe:

start <- Sys.time()
police_calls$categories <- types[police_calls$Incident_Type]
categories_time <- Sys.time() - start
cat("It took", as.numeric(categories_time, units = "secs"), "seconds to put the incidents into super-categories bins.")
It took 1.787194 seconds to put the incidents into super-categories bins.


This is what it looks like in the final UI.

2.4 Extracting latitude and longitude

The column “Location” has the latitude and longitude of the incident:

police_calls$Location[1]
[1] "(28.5224150600001, -81.4712688539999)"


We want to put them in separate columns.

# Using the sub() function with regular expressions
# http://stackoverflow.com/questions/17215789/extract-a-substring-in-r-according-to-a-pattern
# http://www.endmemo.com/program/R/sub.php
start <- Sys.time()
police_calls$latitude <- sub("[(]", "", police_calls$Location)
police_calls$latitude <- sub(",.*", "", police_calls$latitude)
police_calls$latitude <- as.numeric(police_calls$latitude)
police_calls$longitude <- sub(".* ", "", police_calls$Location)
police_calls$longitude <- sub(")", "", police_calls$longitude)
police_calls$longitude <- as.numeric(police_calls$longitude)
latlong_time <- Sys.time() - start
cat("It took", as.numeric(latlong_time, units = "secs"), "seconds to extract the latitudes and longitudes.")
It took 24.23918 seconds to extract the latitudes and longitudes.

2.5 Nulling unused columns

Null columns we no longer need:

# This is better than police_calls$Location <- NULL, etc.
# NULLing them gives some warnings with tibbles for some reason
police_calls <- within(police_calls, rm(Status, Location, Incident_Disposition, Incident_Disposition_Class))

3.0 Missing data

3.1 Subsetting dataset’s unmapped locations

The dataset has some 96,000 missing locations, or about 3% of all the observations.

police_calls[is.na(police_calls$latitude),] %>% nrow()
[1] 96377


Replacing all 96,000 of them would be too much, but I wanted to see if I could replace a large number of them with moderate effort.

The incidents with missing locations are those for which the officer recorded a location that the mapping software couldn’t assign coordinates to. We can take a look at the most frequent of these.

police_calls[is.na(police_calls$latitude),]["Incident_Location"] %>% table() %>% sort(decreasing = T) %>% head()
.
                EW EBO / CONWAY RD            3800 Block of VISION BV 
                              2421                               1054 
  N IVEY LN / OLD WINTER GARDEN RD        NARCOOSSEE RD / BEELINE EBO 
                               974                                959 
           COLUMBIA ST / S IVEY LN S JOHN YOUNG PY / FIRST BAPTIST ST 
                               923                                903 


So 2,421 observations with Incident_Location = “EW EBO / CONWAY RD” do not have coordinates assigned to them. In Orlando, “EW EBO” is the East-West Expressway, Eastbound, also known as the 408 Eastbound. So if I went to Google Maps and manually got the coordinates for that intersection and entered them into the dataset, I could add 2,421 records to the dataset.

So we can create a file that has only the unmapped locations. Start by subsetting the data.

location_names <- police_calls[is.na(police_calls$latitude),]["Incident_Location"] %>% table() %>% sort(decreasing = T) %>% names()
str(location_names)
 chr [1:23493] "EW EBO / CONWAY RD" "3800 Block of VISION BV" ...


Then write the file.

write_csv(as.data.frame(location_names), "unmapped_locations.csv")


3.2 Imputing missing values

Start by loading dataset with some of the locations mapped manually as described in the previous section.

unmapped_locations <- read_csv("unmapped_locations_2017_0213.csv")


Do a little manipulation of the coordinates column to get latitude and longtitude

unmapped_locations$latitude <- sub(",.*", "", unmapped_locations$coordinates)
unmapped_locations$latitude <- as.numeric(unmapped_locations$latitude)
unmapped_locations$longitude <- sub(".* ", "", unmapped_locations$coordinates)
unmapped_locations$longitude <- as.numeric(unmapped_locations$longitude)


start <- Sys.time()
# This is doing a LEFT JOIN
# https://www.w3schools.com/sql/sql_join_left.asp
police_calls_merge <- merge(x = police_calls, y = unmapped_locations[, c("Incident_Location", "latitude", "longitude")], by = "Incident_Location", all.x = TRUE, incomparables = NA)
# After we join, we are left latitude.x from police_calls and latitude.y from
# unmapped_locations. We want a single field, latitude, which takes the value 
# latitude.x if latitude.x is not NA; otherwise it takes the value latitude.y. 
# https://stackoverflow.com/questions/7488068/test-for-na-and-select-values-based-on-result
# I think we can also do:
# police_calls_merge$latitude <- ifelse(!is.na(police_calls_merge$latitude.x), police_calls_merge$latitude.x, police_calls_merge$latitude.y)
police_calls_merge <- within(police_calls_merge, latitude <- ifelse(!is.na(latitude.x), latitude.x, latitude.y))
# Doing the same thing for the longitude
police_calls_merge <- within(police_calls_merge, longitude <- ifelse(!is.na(longitude.x), longitude.x, longitude.y))
# Removing latitude.x, latitude.y, longitude.x, longitude.y
# https://stackoverflow.com/questions/4605206/drop-data-frame-columns-by-name
police_calls_merge <- within(police_calls_merge, rm(latitude.x, latitude.y, longitude.x, longitude.y))
# Ordering by incident number. I don't think you really need to do this, but it looks
# nicer to have the dataframe ordered by the incident number
# https://stackoverflow.com/questions/1296646/how-to-sort-a-dataframe-by-columns
police_calls_merge <- police_calls_merge[with(police_calls_merge, order(Incident_Number)), ]
# Reordering columns, not necessary, either, but it is nicer to have the Incident_Number
# Incident_Date_Time as the first 2 columns
police_calls_merge <- police_calls_merge[, c(2,3,1,4,5:9)]
merge_time <- Sys.time() - start
cat("It took", as.numeric(merge_time, units = "secs"), "seconds to merge the 2 dataframes.")
It took 218.4588 seconds to merge the 2 dataframes.


How many missing records now?

police_calls_merge[is.na(police_calls_merge$latitude),] %>% nrow()
[1] 42703


Over 50,000 records, or more than half the original total of 96,377, have been restored.

4.0 Splitting the file by year

In the final app, I want several files, each corresponding to a year from 2009 to 2015, so the app loads faster. Loading a 500 MB file and performing searches on it in real time is prohibitively slow.

Write the files. It’s a for loop, but a very short one.

start <- Sys.time()
for(year in c(2009:2015)) {
  calls <- police_calls_merge[substr(police_calls_merge$Incident_Number, 1, 4) == as.character(year),]
  calls <- within(calls, rm(Incident_Number))
  write_csv(as.data.frame(calls), paste("calls_", as.character(year), ".csv", sep=""))
}
split_files_time <- Sys.time() - start
cat("It took", as.numeric(split_files_time, units = "secs"), "seconds to split into yearly files.")
It took 45.49237 seconds to split into yearly files.


Wrapping up, compute the time it took to run this munging notebook.

total_time <- (read_time + rename_time + weekdays_time + hours_time + periods_time + categories_time + latlong_time + merge_time + split_files_time)
cat("It took approximately", as.numeric(total_time, units = "secs") / 60 ,"minutes to run this notebook.")
It took approximately 7.973526 minutes to run this notebook.


5.0 References

  1. Orlando Police Department. (2016). OPD Calls for Service Data [CSV]. Retrieved from https://data.cityoforlando.net/Orlando-Police/OPD-Calls-for-Service-Data-Lens/uum9-29mz).

  2. Eduardo Ariño de la Rubia and Sheila Doshi. A Huge Debate: R vs. Python for Data Science [Video] Retrieved from https://blog.dominodatalab.com

  3. remi and lukeA. Read csv file hosted on Google Drive Retrieved from http://stackoverflow.com

  4. Nithinbemitk, Victor Sharovatov, and Tomasz Gandor. Disabling the large file notification from google drive. Retrieved from http://stackoverflow.com

  5. screechOwl and Side_0o_Effect. How to rename a single column in a data.frame?. Retrieved from http://stackoverflow.com

  6. Galilean Moons and Tyler Rinker. Convert 12 hour character time to 24 hour. Retrieved from http://stackoverflow.com

  7. Ivri and Calimo. Working with dictionaries/lists in R. Retrieved from http://stackoverflow.com

  8. alittleboy and G. Grothendieck. extract a substring in R according to a pattern. Retrieved from http://stackoverflow.com.

  9. endmemo. R sub Function. Retrieved from http://www.endmemo.com

  10. Alphaneo and Dirk Eddelbuettel. Global variables in R. Retrieved from http://stackoverflow.com.

  11. Tor and Shane. Suppress one command’s output in R. Retrieved from http://stackoverflow.com.

  12. w3schools. SQL LEFT JOIN Keyword. Retrieved from https://www.w3schools.com

  13. Dan and Joris Meys. Test for NA and select values based on result. Retrieved from http://stackoverflow.com.

  14. Btibert3 and Joris Meys. Drop data frame columns by name. Retrieved from http://stackoverflow.com.

  15. Christopher DuBois and Dirk Eddelbuettel. How to sort a dataframe by column(s)?. Retrieved from http://stackoverflow.com.

  16. Chang, Winston. Cookbook for R. Sebastopol: O’Reilly Media, 2013. Retrieved from http://www.cookbook-r.com/

LS0tDQp0aXRsZTogIkRhdGEgbXVuZ2luZzogT3JsYW5kbyBwb2xpY2UgY2FsbHMiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmJvZHksIHRkIHsNCiAgIGZvbnQtc2l6ZTogMThweDsNCn0NCmgxIHsNCiAgZm9udC1zaXplOiAzMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgyIHsNCiAgZm9udC1zaXplOiAyOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgzIHsNCiAgZm9udC1zaXplOiAyNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmg0IHsNCiAgZm9udC1zaXplOiAyMHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmNvZGUucnsNCiAgZm9udC1zaXplOiAxNnB4Ow0KfQ0KcHJlIHsNCiAgZm9udC1zaXplOiAxNnB4DQp9DQo8L3N0eWxlPg0KDQojIyAxLjAgSW50cm9kdWN0aW9uDQoNClRoaXMgbm90ZWJvb2sgc2hvd3MgdGhlIHByb2NlZHVyZSBmb2xsb3dlZCB0byBtdW5nZSB0aGUgZGF0YXNldCBvZiBbT3JsYW5kbyBwb2xpY2UgY2FsbHMgZnJvbSAyMDA5IHRvIDIwMTVdKGh0dHBzOi8vZGF0YS5jaXR5b2ZvcmxhbmRvLm5ldC9PcmxhbmRvLVBvbGljZS9PUEQtQ2FsbHMtZm9yLVNlcnZpY2UtRGF0YS1MZW5zL3V1bTktMjlteiksIHdoaWNoIEkgdXNlZCB0byBwdXQgdG9nZXRoZXIgYSBbcG9saWNlIGNhbGxzIG1hcF0oaHR0cHM6Ly9jYXJsb3NnZy5zaGlueWFwcHMuaW8vb3JsYW5kby1wb2xpY2UtY2FsbHMtbWFwLykuDQoNCiMjIyAxLjEgUmVhZGluZyB0aGUgZGF0YQ0KDQpJIHN0b3JlZCB0aGUgZGF0YXNldCBpbiBbR29vZ2xlIERyaXZlXShodHRwczovL2RyaXZlLmdvb2dsZS5jb20vdWM/ZXhwb3J0PWRvd25sb2FkJmNvbmZpcm09R0IwdCZpZD0xX1QteDEwV0tQQ0F0YnBzVFQtbHRORWlpS3BRdExSYlUpLCBqdXN0IGNsaWNrIHRvIGRvd25sb2FkIHRoZSBmaWxlLiBZb3UgbWF5IGdldCBhIHdhcm5pbmcgdGhhdCB0aGUgZmlsZSBpcyB0b28gYmlnIGZvciBHb29nbGUgdG8gc2NhbiBmb3IgdmlydXNlcywgc28gaXQgYXNrcyB5b3UgaWYgeW91IHdhbnQgdG8gZG93bmxvYWQgaXQgYW55d2F5LiANCg0KIVtdKGdvb2dsZV9kcml2ZV93YXJuaW5nLlBORyAiR29vZ2xlIERyaXZlIFdhcm5pbmciKQ0KPGJyPg0KDQpKdXN0IGNsaWNrIHdoZXJlIGl0IHNheXMgIkRvd25sb2FkIGFueXdheSIuIEl0J3MgW3RyaWNreV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTQ3MjgwMzgvZGlzYWJsaW5nLXRoZS1sYXJnZS1maWxlLW5vdGlmaWNhdGlvbi1mcm9tLWdvb2dsZS1kcml2ZSkgdG8gZ2V0IGFyb3VuZCB0aGF0IHdhcm5pbmcgaW4gUiBhbmQganVzdCByZWFkIHRoZSBmaWxlIGZyb20gR29vZ2xlIERyaXZlIHRvIHRoZSBjb21wdXRlciwgYmVjYXVzZSBpdCBzZWVtcyBHb29nbGUgcGVyaW9kaWNhbGx5IGNoYW5nZXMgdGhpbmdzIGFyb3VuZC4gSGVuY2UsIEkgdGhpbmsgdGhlIHNhZmVzdCByZXByb2R1Y2libGUgd2F5IGlzIHRvIG1hbnVhbGx5IGRvd25sb2FkIHRoZSBmaWxlIHRvIHlvdXIgY29tcHV0ZXIgYXMgZGVzY3JpYmVkIGFib3ZlLg0KDQpUaGUgZGF0YXNldCBjbG9ja3MgaW4gYXQgNDc0IE1CLCBzbyBpdCB0YWtlcyBzb21lIHRpbWUgdG8gZG93bmxvYWQuIEFsc28sIGV2ZW4gaWYgdGhlIGZpbGUgd2FzIHN0b3JlZCBsb2NhbGx5LCB1c2luZyB0aGUgc3RhbmRhcmQgYHJlYWQuY3N2KClgIHRha2VzIGEgbG9uZyB0aW1lIHRvIHJlYWQgaW4gc3VjaCBhIG1hbW1vdGggZmlsZS4gSW5zdGVhZCwgd2UgY2FuIHVzZSBgcmVhZF9jc3YoKWAgZnJvbSB0aGUgW2ByZWFkciBwYWNrYWdlYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JlYWRyL3JlYWRyLnBkZikuIE9uIG15IGNvbXB1dGVyLCBgcmVhZF9jc3YoKWAgaXMgYWJvdXQgMyB0aW1lcyBmYXN0ZXIgdGhhbiBgcmVhZC5jc3YoKWAuIEkgYXNzdW1lIHlvdSBjbGlja2VkIG9uIHRoZSBHb29nbGUgRHJpdmUgbGluayBhYm92ZSBhbmQgZG93bmxvYWRlZCB0aGUgZmlsZSBsb2NhbGx5LiBUaHJvdWdob3V0IHRoZSBub3RlYm9vaywgSSB3aWxsIGtlZXAgdHJhY2sgb2YgaG93IGxvbmcgaXQgdGFrZXMgdG8gZG8gdGhlIHZhcmlvdXMgb3BlcmF0aW9ucyBvbiBteSBjb21wdXRlci4NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHJlYWRyKQ0Kc3RhcnQgPC0gU3lzLnRpbWUoKQ0KcG9saWNlX2NhbGxzIDwtIHJlYWRfY3N2KCJDYWxsc19Gb3JfU2VydmljZV8yMDA5Xy1fMjAxNS5jc3YiKQ0KcmVhZF90aW1lIDwtIFN5cy50aW1lKCkgLSBzdGFydA0KY2F0KCJJdCB0b29rIiwgYXMubnVtZXJpYyhyZWFkX3RpbWUsIHVuaXRzID0gInNlY3MiKSwgInNlY29uZHMgdG8gcmVhZCB0aGUgZmlsZS4iKQ0KYGBgDQo8YnI+DQoNClRha2UgYSBsb29rIGF0IHRoZSBkYXRhOg0KYGBge3J9DQpzdHIocG9saWNlX2NhbGxzLCBnaXZlLmF0dHIgPSBGQUxTRSkNCmBgYA0KPGJyPg0KDQpTbyB3ZSBoYXZlIHNvbWUgMyBtaWxsaW9uIG9ic2VydmF0aW9ucyBhbmQgOCBjb2x1bW5zLg0KDQojIyAyLjAgTXVuZ2luZw0KDQpMb2FkaW5nIHNvbWUgbGlicmFyaWVzIHdlIHdpbGwgYmUgdXNpbmcNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShjaHJvbikgIyBmb3IgZGVhbGluZyB3aXRoIGNocm9ub2xvZ2ljYWwgb2JqZWN0cywgaS5lLiwgZGF0ZXMgYW5kIHRpbWVzDQpsaWJyYXJ5KG1hZ3JpdHRyKSAjIHBpcGUgb3BlcmF0b3IgJT4lDQpgYGANCjxicj4NCg0KIyMjIDIuMSBSZW5hbWluZyBjb2x1bW5zDQoNClJlbmFtZSBjb2x1bW5zIHRvIHJlbW92ZSBzcGFjZXM6DQpgYGB7cn0NCnN0YXJ0IDwtIFN5cy50aW1lKCkNCm5hbWVzKHBvbGljZV9jYWxscylbbmFtZXMocG9saWNlX2NhbGxzKSA9PSAnSW5jaWRlbnQgTnVtYmVyJ10gPC0gJ0luY2lkZW50X051bWJlcicNCm5hbWVzKHBvbGljZV9jYWxscylbbmFtZXMocG9saWNlX2NhbGxzKSA9PSAnSW5jaWRlbnQgRGF0ZSBUaW1lJ10gPC0gJ0luY2lkZW50X0RhdGVfVGltZScNCm5hbWVzKHBvbGljZV9jYWxscylbbmFtZXMocG9saWNlX2NhbGxzKSA9PSAnSW5jaWRlbnQgTG9jYXRpb24nXSA8LSAnSW5jaWRlbnRfTG9jYXRpb24nDQpuYW1lcyhwb2xpY2VfY2FsbHMpW25hbWVzKHBvbGljZV9jYWxscykgPT0gJ0luY2lkZW50IFR5cGUnXSA8LSAnSW5jaWRlbnRfVHlwZScNCm5hbWVzKHBvbGljZV9jYWxscylbbmFtZXMocG9saWNlX2NhbGxzKSA9PSAnSW5jaWRlbnQgRGlzcG9zaXRpb24gQ2xhc3MnXSA8LSAnSW5jaWRlbnRfRGlzcG9zaXRpb25fQ2xhc3MnDQpuYW1lcyhwb2xpY2VfY2FsbHMpW25hbWVzKHBvbGljZV9jYWxscykgPT0gJ0luY2lkZW50IERpc3Bvc2l0aW9uJ10gPC0gJ0luY2lkZW50X0Rpc3Bvc2l0aW9uJw0KcmVuYW1lX3RpbWUgPC0gU3lzLnRpbWUoKSAtIHN0YXJ0DQpjYXQoIkl0IHRvb2siLCByZW5hbWVfdGltZSwgInNlY29uZHMgdG8gcmVuYW1lIHRoZSBjb2x1bW5zLiIpDQpgYGANCg0KIyMjIDIuMiBEYXRlcyBhbmQgdGltZXMNCg0KSW4gdGhlIFthcHBsaWNhdGlvbl0oaHR0cHM6Ly9jYXJsb3NnZy5zaGlueWFwcHMuaW8vb3JsYW5kby1wb2xpY2UtY2FsbHMtbWFwLykgSSBwbGFuIHRvIHVzZSB0aGUgZGF0YSBmb3IsIHdlIG5lZWQgdGhlIGRheSBvZiB0aGUgd2VlayB0aGUgaW5jaWRlbnRzIHRvb2sgcGxhY2UsIGkuZS4sIE1vbmRheSwgVHVlc2RheSwgLi4uIFRoZSBgZGF0ZXNgIGZ1bmN0aW9uIGZyb20gdGhlICoqY2hyb24qKiBwYWNrYWdlIGNvbnZlcnRzICJtbS9kZC95eXl5IiBpbnRvIGEgImRhdGVzIiBvYmplY3QsIHdoaWNoIGluIHR1cm4gaXMgY29udmVydGVkIHRvIGEgIkRhdGUiIG9iamVjdCBieSBgYXMuRGF0ZSgpYC4gVGhlbiBgd2Vla2RheXMoKWANCg0KYGBge3J9DQpzdGFydCA8LSBTeXMudGltZSgpDQojR2V0IHRoZSBkYXRlcyBvZiBpbmNpZGVudHMNCnBvbGljZV9jYWxsc19kYXRlcyA8LSBzdWJzdHIocG9saWNlX2NhbGxzJEluY2lkZW50X0RhdGVfVGltZSwgMSwgMTApICU+JSBkYXRlcygpICU+JSBhcy5EYXRlKCIlbS8lZC8leSIpDQojIyBzdHIocG9saWNlX2NhbGxzX2RhdGVzKQ0KIyMgIERhdGVbMTozMDUxMjY1XSwgZm9ybWF0OiAiMjAwOS0wMS0wMSIgIjIwMDktMDEtMDEiICIyMDA5LTAxLTAxIiAiMjAwOS0wMS0wMSIgIjIwMDktMDEtMDEiIC4uLg0KDQojQ3JlYXRlIG5ldyBjb2x1bW4gY29uc2lzdGluZyBvZiBkYXlzIG9mIHdlZWsgaW5jaWRlbnRzIHRvb2sgcGxhY2UNCiMgd2Vla2RheXMoeXl5eS1tbS1kZCkgcmV0dXJucyB0aGUgZGF5IG9mIHRoZSB3ZWVrIG9mIHRoYXQgZGF0ZQ0KcG9saWNlX2NhbGxzJGRheXMgPC0gd2Vla2RheXMocG9saWNlX2NhbGxzX2RhdGVzKSAjIHZlY3RvciBvZiAiV2VkbmVzZGF5IiAsICJTYXR1cmRheSIsIC4uLg0Kd2Vla2RheXNfdGltZSA8LSBTeXMudGltZSgpIC0gc3RhcnQNCmNhdCgiSXQgdG9vayIsIHdlZWtkYXlzX3RpbWUsICJzZWNvbmRzIHRvIGNvbXB1dGUgdGhlIHdlZWtkYXlzIGZyb20gdGhlIGRhdGVzLiIpDQpgYGANCjxicj4NCg0KTm93IHdlIHdhbnQgdG8gZXh0cmFjdCB0aGUgaG91ciBpbiB3aGljaCB0aGUgZXZlbnRzIHRvb2sgcGxhY2UuIFdlIHdpbGwgYmUgdXNpbmcgMjQtaG91ciBmb3JtYXQsIHNvIDQgUC5NLiB3aWxsIGJlIDE2LiANCmBgYHtyfQ0Kc3RhcnQgPC0gU3lzLnRpbWUoKQ0KI0dldCB0aGUgdGltZXMsIGluIDI0LWhvdXIgZm9ybWF0LCB0aGUgaW5jaWRlbnRzIHRvb2sgcGxhY2UNCiNVc2luZyBzdHJwdGltZSgpIHBlciB0aGlzIFMuTy4gcG9zdDoNCiNodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzI5ODMzNTM4L2NvbnZlcnQtMTItaG91ci1jaGFyYWN0ZXItdGltZS10by0yNC1ob3VyDQpwb2xpY2VfY2FsbHNfdGltZXMgPC0gc3Vic3RyKHBvbGljZV9jYWxscyRJbmNpZGVudF9EYXRlX1RpbWUsIDEyLDIyKSAlPiUgc3RycHRpbWUoIiVJOiVNOiVTICVwIikgJT4lIGZvcm1hdChmb3JtYXQgPSAiJUg6JU06JVMiKSAlPiUgdGltZXMoKQ0KIyMgc3RyKHBvbGljZV9jYWxsc190aW1lcywgZ2l2ZS5hdHRyID0gRkFMU0UpDQojIyBDbGFzcyAndGltZXMnICBhdG9taWMgWzE6MzA1MTI2NV0gMC4wMDA2OTQgMC4wMDA2OTQgMC4wMDEzODkgMC4wMDEzODkgMC4wMDIwODMgLi4uDQojQ3JlYXRlIG5ldyBjb2x1bW4gY29uc2lzdGluZyBvZiBob3VyIG9mIGRheSBpbmNpZGVudHMgdG9vayBwbGFjZQ0KcG9saWNlX2NhbGxzX2hvdXJzIDwtIGhvdXJzKHBvbGljZV9jYWxsc190aW1lcykNCmhvdXJzX3RpbWUgPC0gU3lzLnRpbWUoKSAtIHN0YXJ0DQpjYXQoIkl0IHRvb2siLCBhcy5udW1lcmljKGhvdXJzX3RpbWUsIHVuaXRzID0gIm1pbnMiKSwgIm1pbnV0ZXMgdG8gY29tcHV0ZSB0aGUgaG91cnMgZnJvbSB0aGUgdGltZXMuIikNCmBgYA0KDQpOb3cgd2Ugd2FudCB0byB1c2UgYHBvbGljZV9jYWxsc19ob3Vyc2AgdG8gY29tcHV0ZSB0aGUgdGltZSBwZXJpb2QgaW4gd2hpY2ggdGhlIGV2ZW50cyB0b29rIHBsYWNlOiAiZWFybHlfbW9ybmluZyIsICJtb3JuaW5nIiwgImFmdGVybm9vbiIsIGFuZCAiZXZlbmluZyIuDQoNCmBgYHtyfQ0Kc3RhcnQgPC0gU3lzLnRpbWUoKQ0KI0NyZWF0ZSBhIG5ldyBjb2x1bW4gY29uc2lzdGluZyBvZiB0aW1lIHBlcmlvZCBpbmNpZGVudHMgdG9vayBwbGFjZQ0KIyBEZXRlcm1pbmUgcGVyaW9kcyBhcyBmb2xsb3dzOg0KIyBFYXJseSBNb3JuaW5nIChiZWZvcmUgNiBBTSk6IDAsIE1vcm5pbmcgKGJldHdlZW4gNiBBTSBhbmQgMTIgUE0pOiAxDQojIEFmdGVybm9vbiAoYmV0d2VlbiBub29uIGFuZCA2IFBNKTogMiwgRXZlbmluZyAoYmV0d2VlbiA2IFBNIGFuZCBtaWRuaWdodCk6IDMNCiMgRGVmaW5pbmcgYSBmdW5jdGlvbiB0aGF0IGRvZXMganVzdCB0aGF0DQpob3Vyc190b19wZXJpb2RzIDwtIGZ1bmN0aW9uKGhvdXIpIHtpZiAoaG91ciAlLyUgNiA9PSAwKSAiZWFybHlfbW9ybmluZyIgZWxzZSBpZiAoaG91ciAlLyUgNiA9PSAxKSAibW9ybmluZyIgZWxzZSBpZiAoaG91ciAlLyUgNiA9PSAyKSAiYWZ0ZXJub29uIiBlbHNlICJldmVuaW5nIn0NCiMgQXBwbHlpbmcgdGhlIGZ1bmN0aW9uIHRvIHRoZSBwb2xpY2VfY2FsbHNfaG91cnMgdmVjdG9yDQpwb2xpY2VfY2FsbHMkcGVyaW9kcyA8LSBzYXBwbHkocG9saWNlX2NhbGxzX2hvdXJzLCBob3Vyc190b19wZXJpb2RzKQ0KcGVyaW9kc190aW1lIDwtIFN5cy50aW1lKCkgLSBzdGFydA0KY2F0KCJJdCB0b29rIiwgYXMubnVtZXJpYyhwZXJpb2RzX3RpbWUsIHVuaXRzID0gInNlY3MiKSwgInNlY29uZHMgdG8gY29tcHV0ZSB0aGUgaG91cnMgZnJvbSB0aGUgdGltZXMuIikNCmBgYA0KDQojIyMgMi4zIENpcmN1bXNjcmliaW5nIGluY2lkZW50cyBpbnRvIGNhdGVnb3JpZXMNCg0KSSB1bHRpbWF0ZWx5IHdhbnQgdG8gZGVzaWduIGEgVUkgdGhhdCBhbGxvd3MgdXNlcnMgdG8gY2xpY2sgb24gdGhlIGluY2lkZW50IGNhdGVnb3JpZXMgdGhleSB3YW50IGRpc3BsYXllZCwgdXNpbmcgY2hlY2tib3hlcy4gVGhlIGRhdGEgYXMgZ2l2ZW4gaGFzIHRvbyBtYW55IGluY2lkZW50IHR5cGVzIHRvIGFsbG93IHRoYXQuDQoNCmBgYHtyfQ0KbnVtX3R5cGVzIDwtIGxlbmd0aCh1bmlxdWUocG9saWNlX2NhbGxzJEluY2lkZW50X1R5cGUpKQ0KY2F0KCJUaGVyZSBhcmUiLCBudW1fdHlwZXMsICJpbmNpZGVudCB0eXBlcyBpbiB0aGUgZGF0YXNldC4iKQ0KYGBgDQo8YnI+DQoNCkl0IGlzIG5vdCBwcmFjdGljYWwgdG8gaW5jbHVkZSAxNDIgY2hlY2tib3hlcyBpbiBhIFVJLCBzbyBJIHdhbnQgdG8gYWdncmVnYXRlIHRoZW0gaW50byBhIGZldyBjYXRlZ29yaWVzIHNvIHRoYXQgdGhlIFVJIHdvbid0IGJlIG92ZXJ3aGVsbWluZy4gSGVyZSBpcyB0aGUgbGlzdCBvZiBhbGwgdGhlIGluY2lkZW50IHR5cGVzLg0KDQpgYGB7cn0NCnR5cGVzIDwtIHJlcCgicGxhY2Vob2xkZXIiLCBudW1fdHlwZXMpICNjcmVhdGVzIHZlY3RvciBvZiAicGxhY2Vob2xkZXIiIHJlcGVhdGVkIG51bV90eXBlcyB0aW1lcw0KbmFtZXModHlwZXMpIDwtIGFzLnZlY3Rvcih1bmlxdWUocG9saWNlX2NhbGxzJEluY2lkZW50X1R5cGUpKSAjIGFkZHMgbmFtZXMgdG8gdmVjdG9yIGp1c3QgY3JlYXRlZA0KIyB0eXBlcyBpcyBhIG5hbWVkIHZlY3Rvci4gSXQncyBhIGxpdHRsZSBsaWtlIGEgZGljdGlvbmFyeSBpbiBQeXRob24uIFRoZSBuYW1lcyBvZiB0aGUgdmVjdG9yDQojIGFyZSBsaWtlIGEgZGljdGlvbmFyeSdzIGtleXMgaW4gUHl0aG9uDQojIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzI4NTgwMTQvd29ya2luZy13aXRoLWRpY3Rpb25hcmllcy1saXN0cy1pbi1yDQojIyB0eXBlc1sxOjNdDQojIyBOb24tZW1lcmdlbmN5IGFzc2lzdGFuY2UgICAgICAgICAgVW5rbm93biB0cm91YmxlICAgICAgICBTdXNwaWNpb3VzIHBlcnNvbiANCiMjICAgICAgICAgICAgInBsYWNlaG9sZGVyIiAgICAgICAgICAgICJwbGFjZWhvbGRlciIgICAgICAgICAgICAicGxhY2Vob2xkZXIiDQojIyB0eXBlc1siVW5rbm93biB0cm91YmxlIl0NCiMjIFVua25vd24gdHJvdWJsZSANCiMjICAgInBsYWNlaG9sZGVyIg0KbmFtZXModHlwZXMpDQpgYGANCjxicj4NCg0KV2Ugd2lsbCBwdXQgdGhlbSBpbnRvIHNvbWUgZ2VuZXJhbCBjYXRlZ29yaWVzLiBGb3IgZXhhbXBsZSwgIlN1c3BpY2lvdXMgcGVyc29uIiAoIzMpLCAiU3VzcGljaW91cyBpbmNpZGVudCIoIzUpLCAiU3VzcGljaW91cyB2ZWhpY2xlIigjMzApLCBhbmQgb3RoZXJzIHNpbWlsYXIgY291bGQgYmUgcHV0IGludG8gYSBzaW5nbGUgYmluIGNhbGxlZCAic3VzcGljaW91cyIuIExpa2V3aXNlLCAiR2VuZXJhbCBkaXN0dXJiYW5jZSIoIzQpLCAiR2VuZXJhbCBkaXN0dXJiYW5jZSAtIGFybWVkIigjMjIpLCBhbmQgIkRvbWVzdGljIGRpc3R1cmJhbmNlIigjNDEpIGNvdWxkIGFsbCBiZSBmaWxlZCB1bmRlciAiZGlzdHVyYmFuY2UiLg0KDQpgYGB7cn0NCiMgV2UgYXJlIGNoYW5naW5nIHRoZSB2YWx1ZXMgb2YgdGhlc2UgImtleXMiICh0aGUgbmFtZXMgaW4gdGhlIG5hbWVkIHZlY3RvcikgdG8gYmUgb3VyIHN1cGVyLWNhdGVnb3JpZXMsIGkuZS4sICJzdXNwaWNpb3VzIiwgImRpc3R1cmJhbmNlIiwgImV0Yy4iDQp0eXBlc1tjKDE6MiwgNywgOSwgMTMsIDE3OjE4LCAyMDoyMSwgMjMsIDM1LCA0MCwgNDIsIDQ5OjUxLCA1NDo1NywgNTk6NjAsIDYyOjYzLCA2Njo2OCwgNzAsIDc4OjgyLCA4NDo4NSwgODc6OTAsIDk0LCA5NiwgOTksIDEwNSwgMTA3LCAxMDk6MTEwLCAxMTU6MTE2LCAxMjA6MTIxLCAxMjM6MTI0LCAxMjc6MTI4LCAxMzAsIDEzMywgMTM3LCAxNDAsIDE0MildID0gIm90aGVyIg0KdHlwZXNbYygzLCA1LCAzMCwgMTAxLCAxMDUsIDExNzoxMTgsIDEyMiwgMTI1KV0gPSAic3VzcGljaW91cyINCnR5cGVzW2MoNCwgMjIsIDQxKV0gPSAiZGlzdHVyYmFuY2UiDQp0eXBlc1tjKDYsIDExLCAxNSwgNjQsIDgzLCAxMDAsIDExMSldID0gImFsYXJtIg0KdHlwZXNbYyg4LCAyNCwgNDgsIDkxLCAxMTMpXSA9ICJiYXR0ZXJ5Ig0KdHlwZXNbYygxMCwgMTAzLCAxMTIsIDEzOCldID0gInRyZXNwYXNzIg0KdHlwZXNbYygxMiwgMjAsIDI1OjI3LCAzMiwgMzQsIDM2OjM3LCA0NCwgNTMsIDcxLCA3NSwgODYsIDkyLCA5NSwgOTcsIDEwMiwgMTA4LCAxMTksIDEzMiwgMTM0OjEzNSwgMTQxKV0gPSAidHJhZmZpYyINCnR5cGVzW2MoMTQsIDkzKV0gPSAiYXNzYXVsdCINCnR5cGVzW2MoMTYpXSA9ICJEVUkiDQp0eXBlc1tjKDE5LCAyOSwgNDMsIDQ2KV0gPSAiQiZFIg0KdHlwZXNbYygyOCldID0gIm1pc2NoaWVmIg0KdHlwZXNbYygzMSwgNzYsIDExNCwgMTI2KV0gPSAicm9iYmVyeSINCnR5cGVzW2MoMzMsIDUyLCA1OCwgNjEsIDcyOjczLCAxMDQpXSA9ICJ0aGVmdCINCnR5cGVzW2MoMzgsIDQ1LCA2OSwgMTM5KV0gPSAiZnVnaXRpdmUiDQp0eXBlc1tjKDM5LCAxMjkpXSA9ICJkcnVncyINCnR5cGVzW2MoNDcgKV0gPSAiZnJhdWQiDQp0eXBlc1tjKDYzLCA2NSwgNzcsIDkwLCA5OCwgMTMxKV0gPSAic2V4IGNyaW1lcyINCnR5cGVzW2MoNzQsIDEwNildID0gInN1aWNpZGUiDQp0eXBlc1tjKDEzNildID0gIm11cmRlciINCiMjIHR5cGVzWzE6M10NCiMjIE5vbi1lbWVyZ2VuY3kgYXNzaXN0YW5jZSAgICAgICAgICBVbmtub3duIHRyb3VibGUgICAgICAgIFN1c3BpY2lvdXMgcGVyc29uIA0KIyMgICAgICAgICAgICAgICAgICAib3RoZXIiICAgICAgICAgICAgICAgICAgIm90aGVyIiAgICAgICAgICAgICAic3VzcGljaW91cyIgDQpgYGANCjxicj4NCg0KRmluYWxseSwgd2UgY2FuIGNyZWF0ZSB0aGUgY2F0ZWdvcmllcyBjb2x1bW4gYW5kIGFkZCBpdCB0byB0aGUgcG9saWNlX2NhbGxzIGRhdGFmcmFtZToNCmBgYHtyfQ0Kc3RhcnQgPC0gU3lzLnRpbWUoKQ0KcG9saWNlX2NhbGxzJGNhdGVnb3JpZXMgPC0gdHlwZXNbcG9saWNlX2NhbGxzJEluY2lkZW50X1R5cGVdDQpjYXRlZ29yaWVzX3RpbWUgPC0gU3lzLnRpbWUoKSAtIHN0YXJ0DQpjYXQoIkl0IHRvb2siLCBhcy5udW1lcmljKGNhdGVnb3JpZXNfdGltZSwgdW5pdHMgPSAic2VjcyIpLCAic2Vjb25kcyB0byBwdXQgdGhlIGluY2lkZW50cyBpbnRvIHN1cGVyLWNhdGVnb3JpZXMgYmlucy4iKQ0KYGBgDQo8YnI+DQoNClRoaXMgaXMgd2hhdCBpdCBsb29rcyBsaWtlIGluIHRoZSBbZmluYWwgVUldKGh0dHBzOi8vY2FybG9zZ2cuc2hpbnlhcHBzLmlvL29ybGFuZG8tcG9saWNlLWNhbGxzLW1hcC8pLg0KDQohW10odWlfY2F0ZWdvcmllcy5QTkcgIkdvb2dsZSBEcml2ZSBXYXJuaW5nIikNCg0KIyMjIDIuNCBFeHRyYWN0aW5nIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUNCg0KVGhlIGNvbHVtbiAiTG9jYXRpb24iIGhhcyB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSBvZiB0aGUgaW5jaWRlbnQ6DQoNCmBgYHtyfQ0KcG9saWNlX2NhbGxzJExvY2F0aW9uWzFdDQpgYGANCjxicj4NCg0KV2Ugd2FudCB0byBwdXQgdGhlbSBpbiBzZXBhcmF0ZSBjb2x1bW5zLg0KDQpgYGB7cn0NCiMgVXNpbmcgdGhlIHN1YigpIGZ1bmN0aW9uIHdpdGggcmVndWxhciBleHByZXNzaW9ucw0KIyBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzE3MjE1Nzg5L2V4dHJhY3QtYS1zdWJzdHJpbmctaW4tci1hY2NvcmRpbmctdG8tYS1wYXR0ZXJuDQojIGh0dHA6Ly93d3cuZW5kbWVtby5jb20vcHJvZ3JhbS9SL3N1Yi5waHANCnN0YXJ0IDwtIFN5cy50aW1lKCkNCnBvbGljZV9jYWxscyRsYXRpdHVkZSA8LSBzdWIoIlsoXSIsICIiLCBwb2xpY2VfY2FsbHMkTG9jYXRpb24pDQpwb2xpY2VfY2FsbHMkbGF0aXR1ZGUgPC0gc3ViKCIsLioiLCAiIiwgcG9saWNlX2NhbGxzJGxhdGl0dWRlKQ0KcG9saWNlX2NhbGxzJGxhdGl0dWRlIDwtIGFzLm51bWVyaWMocG9saWNlX2NhbGxzJGxhdGl0dWRlKQ0KcG9saWNlX2NhbGxzJGxvbmdpdHVkZSA8LSBzdWIoIi4qICIsICIiLCBwb2xpY2VfY2FsbHMkTG9jYXRpb24pDQpwb2xpY2VfY2FsbHMkbG9uZ2l0dWRlIDwtIHN1YigiKSIsICIiLCBwb2xpY2VfY2FsbHMkbG9uZ2l0dWRlKQ0KcG9saWNlX2NhbGxzJGxvbmdpdHVkZSA8LSBhcy5udW1lcmljKHBvbGljZV9jYWxscyRsb25naXR1ZGUpDQpsYXRsb25nX3RpbWUgPC0gU3lzLnRpbWUoKSAtIHN0YXJ0DQpjYXQoIkl0IHRvb2siLCBhcy5udW1lcmljKGxhdGxvbmdfdGltZSwgdW5pdHMgPSAic2VjcyIpLCAic2Vjb25kcyB0byBleHRyYWN0IHRoZSBsYXRpdHVkZXMgYW5kIGxvbmdpdHVkZXMuIikNCmBgYA0KDQojIyMgMi41IE51bGxpbmcgdW51c2VkIGNvbHVtbnMNCg0KTnVsbCBjb2x1bW5zIHdlIG5vIGxvbmdlciBuZWVkOg0KYGBge3J9DQojIFRoaXMgaXMgYmV0dGVyIHRoYW4gcG9saWNlX2NhbGxzJExvY2F0aW9uIDwtIE5VTEwsIGV0Yy4NCiMgTlVMTGluZyB0aGVtIGdpdmVzIHNvbWUgd2FybmluZ3Mgd2l0aCB0aWJibGVzIGZvciBzb21lIHJlYXNvbg0KcG9saWNlX2NhbGxzIDwtIHdpdGhpbihwb2xpY2VfY2FsbHMsIHJtKFN0YXR1cywgTG9jYXRpb24sIEluY2lkZW50X0Rpc3Bvc2l0aW9uLCBJbmNpZGVudF9EaXNwb3NpdGlvbl9DbGFzcykpDQpgYGANCg0KIyMgMy4wIE1pc3NpbmcgZGF0YQ0KDQojIyMgMy4xIFN1YnNldHRpbmcgZGF0YXNldCdzIHVubWFwcGVkIGxvY2F0aW9ucw0KDQpUaGUgZGF0YXNldCBoYXMgc29tZSA5NiwwMDAgbWlzc2luZyBsb2NhdGlvbnMsIG9yIGFib3V0IDMlIG9mIGFsbCB0aGUgb2JzZXJ2YXRpb25zLg0KDQpgYGB7cn0NCnBvbGljZV9jYWxsc1tpcy5uYShwb2xpY2VfY2FsbHMkbGF0aXR1ZGUpLF0gJT4lIG5yb3coKQ0KYGBgDQo8YnI+DQoNClJlcGxhY2luZyBhbGwgOTYsMDAwIG9mIHRoZW0gd291bGQgYmUgdG9vIG11Y2gsIGJ1dCBJIHdhbnRlZCB0byBzZWUgaWYgSSBjb3VsZCByZXBsYWNlIGEgbGFyZ2UgbnVtYmVyIG9mIHRoZW0gd2l0aCBtb2RlcmF0ZSBlZmZvcnQuDQoNClRoZSBpbmNpZGVudHMgd2l0aCBtaXNzaW5nIGxvY2F0aW9ucyBhcmUgdGhvc2UgZm9yIHdoaWNoIHRoZSBvZmZpY2VyIHJlY29yZGVkIGEgbG9jYXRpb24gdGhhdCB0aGUgbWFwcGluZyBzb2Z0d2FyZSBjb3VsZG4ndCBhc3NpZ24gY29vcmRpbmF0ZXMgdG8uIFdlIGNhbiB0YWtlIGEgbG9vayBhdCB0aGUgbW9zdCBmcmVxdWVudCBvZiB0aGVzZS4NCg0KYGBge3J9DQpwb2xpY2VfY2FsbHNbaXMubmEocG9saWNlX2NhbGxzJGxhdGl0dWRlKSxdWyJJbmNpZGVudF9Mb2NhdGlvbiJdICU+JSB0YWJsZSgpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUKSAlPiUgaGVhZCgpDQpgYGANCjxicj4NCg0KU28gMiw0MjEgb2JzZXJ2YXRpb25zIHdpdGggSW5jaWRlbnRfTG9jYXRpb24gPSAiRVcgRUJPIC8gQ09OV0FZIFJEIiBkbyBub3QgaGF2ZSBjb29yZGluYXRlcyBhc3NpZ25lZCB0byB0aGVtLiBJbiBPcmxhbmRvLCAiRVcgRUJPIiBpcyB0aGUgRWFzdC1XZXN0IEV4cHJlc3N3YXksIEVhc3Rib3VuZCwgYWxzbyBrbm93biBhcyB0aGUgNDA4IEVhc3Rib3VuZC4gU28gaWYgSSB3ZW50IHRvIEdvb2dsZSBNYXBzIGFuZCBtYW51YWxseSBnb3QgdGhlIGNvb3JkaW5hdGVzIGZvciB0aGF0IGludGVyc2VjdGlvbiBhbmQgZW50ZXJlZCB0aGVtIGludG8gdGhlIGRhdGFzZXQsIEkgY291bGQgYWRkIDIsNDIxIHJlY29yZHMgdG8gdGhlIGRhdGFzZXQuDQo8YnI+DQoNClNvIHdlIGNhbiBjcmVhdGUgYSBmaWxlIHRoYXQgaGFzIG9ubHkgdGhlIHVubWFwcGVkIGxvY2F0aW9ucy4gU3RhcnQgYnkgc3Vic2V0dGluZyB0aGUgZGF0YS4NCg0KYGBge3J9DQpsb2NhdGlvbl9uYW1lcyA8LSBwb2xpY2VfY2FsbHNbaXMubmEocG9saWNlX2NhbGxzJGxhdGl0dWRlKSxdWyJJbmNpZGVudF9Mb2NhdGlvbiJdICU+JSB0YWJsZSgpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUKSAlPiUgbmFtZXMoKQ0Kc3RyKGxvY2F0aW9uX25hbWVzKQ0KYGBgDQo8YnI+DQoNClRoZW4gd3JpdGUgdGhlIGZpbGUuDQoNCmBgYHtyfQ0Kd3JpdGVfY3N2KGFzLmRhdGEuZnJhbWUobG9jYXRpb25fbmFtZXMpLCAidW5tYXBwZWRfbG9jYXRpb25zLmNzdiIpDQpgYGANCjxicj4NCg0KIyMjIDMuMiBJbXB1dGluZyBtaXNzaW5nIHZhbHVlcw0KDQpTdGFydCBieSBsb2FkaW5nIGRhdGFzZXQgd2l0aCBzb21lIG9mIHRoZSBsb2NhdGlvbnMgbWFwcGVkIG1hbnVhbGx5IGFzIGRlc2NyaWJlZCBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbi4NCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdW5tYXBwZWRfbG9jYXRpb25zIDwtIHJlYWRfY3N2KCJ1bm1hcHBlZF9sb2NhdGlvbnNfMjAxN18wMjEzLmNzdiIpDQpgYGANCjxicj4NCg0KRG8gYSBsaXR0bGUgbWFuaXB1bGF0aW9uIG9mIHRoZSBjb29yZGluYXRlcyBjb2x1bW4gdG8gZ2V0IGxhdGl0dWRlIGFuZCBsb25ndGl0dWRlDQpgYGB7cn0NCnVubWFwcGVkX2xvY2F0aW9ucyRsYXRpdHVkZSA8LSBzdWIoIiwuKiIsICIiLCB1bm1hcHBlZF9sb2NhdGlvbnMkY29vcmRpbmF0ZXMpDQp1bm1hcHBlZF9sb2NhdGlvbnMkbGF0aXR1ZGUgPC0gYXMubnVtZXJpYyh1bm1hcHBlZF9sb2NhdGlvbnMkbGF0aXR1ZGUpDQp1bm1hcHBlZF9sb2NhdGlvbnMkbG9uZ2l0dWRlIDwtIHN1YigiLiogIiwgIiIsIHVubWFwcGVkX2xvY2F0aW9ucyRjb29yZGluYXRlcykNCnVubWFwcGVkX2xvY2F0aW9ucyRsb25naXR1ZGUgPC0gYXMubnVtZXJpYyh1bm1hcHBlZF9sb2NhdGlvbnMkbG9uZ2l0dWRlKQ0KYGBgDQo8YnI+DQoNCmBgYHtyfQ0Kc3RhcnQgPC0gU3lzLnRpbWUoKQ0KIyBUaGlzIGlzIGRvaW5nIGEgTEVGVCBKT0lODQojIGh0dHBzOi8vd3d3Lnczc2Nob29scy5jb20vc3FsL3NxbF9qb2luX2xlZnQuYXNwDQpwb2xpY2VfY2FsbHNfbWVyZ2UgPC0gbWVyZ2UoeCA9IHBvbGljZV9jYWxscywgeSA9IHVubWFwcGVkX2xvY2F0aW9uc1ssIGMoIkluY2lkZW50X0xvY2F0aW9uIiwgImxhdGl0dWRlIiwgImxvbmdpdHVkZSIpXSwgYnkgPSAiSW5jaWRlbnRfTG9jYXRpb24iLCBhbGwueCA9IFRSVUUsIGluY29tcGFyYWJsZXMgPSBOQSkNCg0KIyBBZnRlciB3ZSBqb2luLCB3ZSBhcmUgbGVmdCBsYXRpdHVkZS54IGZyb20gcG9saWNlX2NhbGxzIGFuZCBsYXRpdHVkZS55IGZyb20NCiMgdW5tYXBwZWRfbG9jYXRpb25zLiBXZSB3YW50IGEgc2luZ2xlIGZpZWxkLCBsYXRpdHVkZSwgd2hpY2ggdGFrZXMgdGhlIHZhbHVlIA0KIyBsYXRpdHVkZS54IGlmIGxhdGl0dWRlLnggaXMgbm90IE5BOyBvdGhlcndpc2UgaXQgdGFrZXMgdGhlIHZhbHVlIGxhdGl0dWRlLnkuIA0KIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83NDg4MDY4L3Rlc3QtZm9yLW5hLWFuZC1zZWxlY3QtdmFsdWVzLWJhc2VkLW9uLXJlc3VsdA0KIyBJIHRoaW5rIHdlIGNhbiBhbHNvIGRvOg0KIyBwb2xpY2VfY2FsbHNfbWVyZ2UkbGF0aXR1ZGUgPC0gaWZlbHNlKCFpcy5uYShwb2xpY2VfY2FsbHNfbWVyZ2UkbGF0aXR1ZGUueCksIHBvbGljZV9jYWxsc19tZXJnZSRsYXRpdHVkZS54LCBwb2xpY2VfY2FsbHNfbWVyZ2UkbGF0aXR1ZGUueSkNCnBvbGljZV9jYWxsc19tZXJnZSA8LSB3aXRoaW4ocG9saWNlX2NhbGxzX21lcmdlLCBsYXRpdHVkZSA8LSBpZmVsc2UoIWlzLm5hKGxhdGl0dWRlLngpLCBsYXRpdHVkZS54LCBsYXRpdHVkZS55KSkNCiMgRG9pbmcgdGhlIHNhbWUgdGhpbmcgZm9yIHRoZSBsb25naXR1ZGUNCnBvbGljZV9jYWxsc19tZXJnZSA8LSB3aXRoaW4ocG9saWNlX2NhbGxzX21lcmdlLCBsb25naXR1ZGUgPC0gaWZlbHNlKCFpcy5uYShsb25naXR1ZGUueCksIGxvbmdpdHVkZS54LCBsb25naXR1ZGUueSkpDQojIFJlbW92aW5nIGxhdGl0dWRlLngsIGxhdGl0dWRlLnksIGxvbmdpdHVkZS54LCBsb25naXR1ZGUueQ0KIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80NjA1MjA2L2Ryb3AtZGF0YS1mcmFtZS1jb2x1bW5zLWJ5LW5hbWUNCnBvbGljZV9jYWxsc19tZXJnZSA8LSB3aXRoaW4ocG9saWNlX2NhbGxzX21lcmdlLCBybShsYXRpdHVkZS54LCBsYXRpdHVkZS55LCBsb25naXR1ZGUueCwgbG9uZ2l0dWRlLnkpKQ0KIyBPcmRlcmluZyBieSBpbmNpZGVudCBudW1iZXIuIEkgZG9uJ3QgdGhpbmsgeW91IHJlYWxseSBuZWVkIHRvIGRvIHRoaXMsIGJ1dCBpdCBsb29rcw0KIyBuaWNlciB0byBoYXZlIHRoZSBkYXRhZnJhbWUgb3JkZXJlZCBieSB0aGUgaW5jaWRlbnQgbnVtYmVyDQojIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzEyOTY2NDYvaG93LXRvLXNvcnQtYS1kYXRhZnJhbWUtYnktY29sdW1ucw0KcG9saWNlX2NhbGxzX21lcmdlIDwtIHBvbGljZV9jYWxsc19tZXJnZVt3aXRoKHBvbGljZV9jYWxsc19tZXJnZSwgb3JkZXIoSW5jaWRlbnRfTnVtYmVyKSksIF0NCiMgUmVvcmRlcmluZyBjb2x1bW5zLCBub3QgbmVjZXNzYXJ5LCBlaXRoZXIsIGJ1dCBpdCBpcyBuaWNlciB0byBoYXZlIHRoZSBJbmNpZGVudF9OdW1iZXINCiMgSW5jaWRlbnRfRGF0ZV9UaW1lIGFzIHRoZSBmaXJzdCAyIGNvbHVtbnMNCnBvbGljZV9jYWxsc19tZXJnZSA8LSBwb2xpY2VfY2FsbHNfbWVyZ2VbLCBjKDIsMywxLDQsNTo5KV0NCm1lcmdlX3RpbWUgPC0gU3lzLnRpbWUoKSAtIHN0YXJ0DQpjYXQoIkl0IHRvb2siLCBhcy5udW1lcmljKG1lcmdlX3RpbWUsIHVuaXRzID0gInNlY3MiKSwgInNlY29uZHMgdG8gbWVyZ2UgdGhlIDIgZGF0YWZyYW1lcy4iKQ0KYGBgDQo8YnI+DQoNCkhvdyBtYW55IG1pc3NpbmcgcmVjb3JkcyBub3c/DQpgYGB7cn0NCnBvbGljZV9jYWxsc19tZXJnZVtpcy5uYShwb2xpY2VfY2FsbHNfbWVyZ2UkbGF0aXR1ZGUpLF0gJT4lIG5yb3coKQ0KYGBgDQo8YnI+DQoNCk92ZXIgNTAsMDAwIHJlY29yZHMsIG9yIG1vcmUgdGhhbiBoYWxmIHRoZSBvcmlnaW5hbCB0b3RhbCBvZiA5NiwzNzcsIGhhdmUgYmVlbiByZXN0b3JlZC4NCg0KIyMgNC4wIFNwbGl0dGluZyB0aGUgZmlsZSBieSB5ZWFyDQoNCkluIHRoZSBmaW5hbCBhcHAsIEkgd2FudCBzZXZlcmFsIGZpbGVzLCBlYWNoIGNvcnJlc3BvbmRpbmcgdG8gYSB5ZWFyIGZyb20gMjAwOSB0byAyMDE1LCBzbyB0aGUgYXBwIGxvYWRzIGZhc3Rlci4gTG9hZGluZyBhIDUwMCBNQiBmaWxlIGFuZCBwZXJmb3JtaW5nIHNlYXJjaGVzIG9uIGl0IGluIHJlYWwgdGltZSBpcyBwcm9oaWJpdGl2ZWx5IHNsb3cuIA0KDQpXcml0ZSB0aGUgZmlsZXMuIEl0J3MgYSBgZm9yYCBsb29wLCBidXQgYSB2ZXJ5IHNob3J0IG9uZS4NCmBgYHtyfQ0Kc3RhcnQgPC0gU3lzLnRpbWUoKQ0KZm9yKHllYXIgaW4gYygyMDA5OjIwMTUpKSB7DQogIGNhbGxzIDwtIHBvbGljZV9jYWxsc19tZXJnZVtzdWJzdHIocG9saWNlX2NhbGxzX21lcmdlJEluY2lkZW50X051bWJlciwgMSwgNCkgPT0gYXMuY2hhcmFjdGVyKHllYXIpLF0NCiAgY2FsbHMgPC0gd2l0aGluKGNhbGxzLCBybShJbmNpZGVudF9OdW1iZXIpKQ0KICB3cml0ZV9jc3YoYXMuZGF0YS5mcmFtZShjYWxscyksIHBhc3RlKCJjYWxsc18iLCBhcy5jaGFyYWN0ZXIoeWVhciksICIuY3N2Iiwgc2VwPSIiKSkNCn0NCnNwbGl0X2ZpbGVzX3RpbWUgPC0gU3lzLnRpbWUoKSAtIHN0YXJ0DQpjYXQoIkl0IHRvb2siLCBhcy5udW1lcmljKHNwbGl0X2ZpbGVzX3RpbWUsIHVuaXRzID0gInNlY3MiKSwgInNlY29uZHMgdG8gc3BsaXQgaW50byB5ZWFybHkgZmlsZXMuIikNCmBgYA0KPGJyPg0KDQpXcmFwcGluZyB1cCwgY29tcHV0ZSB0aGUgdGltZSBpdCB0b29rIHRvIHJ1biB0aGlzIG11bmdpbmcgbm90ZWJvb2suDQoNCmBgYHtyfQ0KdG90YWxfdGltZSA8LSAocmVhZF90aW1lICsgcmVuYW1lX3RpbWUgKyB3ZWVrZGF5c190aW1lICsgaG91cnNfdGltZSArIHBlcmlvZHNfdGltZSArIGNhdGVnb3JpZXNfdGltZSArIGxhdGxvbmdfdGltZSArIG1lcmdlX3RpbWUgKyBzcGxpdF9maWxlc190aW1lKQ0KY2F0KCJJdCB0b29rIGFwcHJveGltYXRlbHkiLCBhcy5udW1lcmljKHRvdGFsX3RpbWUsIHVuaXRzID0gInNlY3MiKSAvIDYwICwibWludXRlcyB0byBydW4gdGhpcyBub3RlYm9vay4iKQ0KYGBgDQo8YnI+DQoNCiMjIDUuMCBSZWZlcmVuY2VzDQoNCjEuIE9ybGFuZG8gUG9saWNlIERlcGFydG1lbnQuICgyMDE2KS4gKioqT1BEIENhbGxzIGZvciBTZXJ2aWNlIERhdGEqKiogW0NTVl0uIFJldHJpZXZlZCBmcm9tIGh0dHBzOi8vZGF0YS5jaXR5b2ZvcmxhbmRvLm5ldC9PcmxhbmRvLVBvbGljZS9PUEQtQ2FsbHMtZm9yLVNlcnZpY2UtRGF0YS1MZW5zL3V1bTktMjlteikuDQoNCjIuIEVkdWFyZG8gQXJpw7FvIGRlIGxhIFJ1YmlhIGFuZCBTaGVpbGEgRG9zaGkuICoqKkEgSHVnZSBEZWJhdGU6IFIgdnMuIFB5dGhvbiBmb3IgRGF0YSBTY2llbmNlKioqIFtWaWRlb10gUmV0cmlldmVkIGZyb20gW2h0dHBzOi8vYmxvZy5kb21pbm9kYXRhbGFiLmNvbV0oaHR0cHM6Ly9ibG9nLmRvbWlub2RhdGFsYWIuY29tL3ZpZGVvLWh1Z2UtZGViYXRlLXItdnMtcHl0aG9uLWRhdGEtc2NpZW5jZS8pDQoNCjMuIHJlbWkgYW5kIGx1a2VBLiAqKipSZWFkIGNzdiBmaWxlIGhvc3RlZCBvbiBHb29nbGUgRHJpdmUqKiogUmV0cmlldmVkIGZyb20gW2h0dHA6Ly9zdGFja292ZXJmbG93LmNvbV0oaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMzU0ODMyMS93aGF0LWRvZXMtc2l6ZS1yZWFsbHktbWVhbi1pbi1nZW9tLXBvaW50KQ0KDQo0LiBOaXRoaW5iZW1pdGssIFZpY3RvciBTaGFyb3ZhdG92LCBhbmQgVG9tYXN6IEdhbmRvci4gKioqRGlzYWJsaW5nIHRoZSBsYXJnZSBmaWxlIG5vdGlmaWNhdGlvbiBmcm9tIGdvb2dsZSBkcml2ZSoqKi4gUmV0cmlldmVkIGZyb20gW2h0dHA6Ly9zdGFja292ZXJmbG93LmNvbV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTQ3MjgwMzgvZGlzYWJsaW5nLXRoZS1sYXJnZS1maWxlLW5vdGlmaWNhdGlvbi1mcm9tLWdvb2dsZS1kcml2ZSkNCg0KNS4gc2NyZWVjaE93bCBhbmQgU2lkZV8wb19FZmZlY3QuICoqKkhvdyB0byByZW5hbWUgYSBzaW5nbGUgY29sdW1uIGluIGEgZGF0YS5mcmFtZT8qKiouIFJldHJpZXZlZCBmcm9tIFtodHRwOi8vc3RhY2tvdmVyZmxvdy5jb21dKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzc1MzE4NjgvaG93LXRvLXJlbmFtZS1hLXNpbmdsZS1jb2x1bW4taW4tYS1kYXRhLWZyYW1lKQ0KDQo2LiBHYWxpbGVhbiBNb29ucyBhbmQgVHlsZXIgUmlua2VyLiAqKipDb252ZXJ0IDEyIGhvdXIgY2hhcmFjdGVyIHRpbWUgdG8gMjQgaG91cioqKi4gUmV0cmlldmVkIGZyb20gW2h0dHA6Ly9zdGFja292ZXJmbG93LmNvbV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjk4MzM1MzgvY29udmVydC0xMi1ob3VyLWNoYXJhY3Rlci10aW1lLXRvLTI0LWhvdXIpDQoNCjcuIEl2cmkgYW5kIENhbGltby4gKioqV29ya2luZyB3aXRoIGRpY3Rpb25hcmllcy9saXN0cyBpbiBSKioqLiBSZXRyaWV2ZWQgZnJvbSBbaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yODU4MDE0L3dvcmtpbmctd2l0aC1kaWN0aW9uYXJpZXMtbGlzdHMtaW4tcikNCg0KOC4gYWxpdHRsZWJveSBhbmQgRy4gR3JvdGhlbmRpZWNrLiAqKipleHRyYWN0IGEgc3Vic3RyaW5nIGluIFIgYWNjb3JkaW5nIHRvIGEgcGF0dGVybioqKi4gUmV0cmlldmVkIGZyb20gW2h0dHA6Ly9zdGFja292ZXJmbG93LmNvbV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTcyMTU3ODkvZXh0cmFjdC1hLXN1YnN0cmluZy1pbi1yLWFjY29yZGluZy10by1hLXBhdHRlcm4pLg0KDQo5LiBlbmRtZW1vLiAqKipSIHN1YiBGdW5jdGlvbioqKi4gUmV0cmlldmVkIGZyb20gW2h0dHA6Ly93d3cuZW5kbWVtby5jb21dKGh0dHA6Ly93d3cuZW5kbWVtby5jb20vcHJvZ3JhbS9SL3N1Yi5waHApDQoNCjEwLiBBbHBoYW5lbyBhbmQgRGlyayBFZGRlbGJ1ZXR0ZWwuICoqKkdsb2JhbCB2YXJpYWJsZXMgaW4gUioqKi4gUmV0cmlldmVkIGZyb20gW2h0dHA6Ly9zdGFja292ZXJmbG93LmNvbV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTIzNjYyMC9nbG9iYWwtdmFyaWFibGVzLWluLXIpLg0KDQoxMS4gVG9yIGFuZCBTaGFuZS4gKioqU3VwcHJlc3Mgb25lIGNvbW1hbmQncyBvdXRwdXQgaW4gUioqKi4gUmV0cmlldmVkIGZyb20gW2h0dHA6Ly9zdGFja292ZXJmbG93LmNvbV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjcyMzAzNC9zdXBwcmVzcy1vbmUtY29tbWFuZHMtb3V0cHV0LWluLXIpLg0KDQoxMi4gdzNzY2hvb2xzLiAqKipTUUwgTEVGVCBKT0lOIEtleXdvcmQqKiouIFJldHJpZXZlZCBmcm9tIFtodHRwczovL3d3dy53M3NjaG9vbHMuY29tXShodHRwczovL3d3dy53M3NjaG9vbHMuY29tL3NxbC9zcWxfam9pbl9sZWZ0LmFzcCkNCg0KMTMuIERhbiBhbmQgSm9yaXMgTWV5cy4gKioqVGVzdCBmb3IgTkEgYW5kIHNlbGVjdCB2YWx1ZXMgYmFzZWQgb24gcmVzdWx0KioqLiBSZXRyaWV2ZWQgZnJvbSBbaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83NDg4MDY4L3Rlc3QtZm9yLW5hLWFuZC1zZWxlY3QtdmFsdWVzLWJhc2VkLW9uLXJlc3VsdCkuDQoNCjE0LiBCdGliZXJ0MyBhbmQgSm9yaXMgTWV5cy4gKioqRHJvcCBkYXRhIGZyYW1lIGNvbHVtbnMgYnkgbmFtZSoqKi4gUmV0cmlldmVkIGZyb20gW2h0dHA6Ly9zdGFja292ZXJmbG93LmNvbV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDYwNTIwNi9kcm9wLWRhdGEtZnJhbWUtY29sdW1ucy1ieS1uYW1lKS4NCg0KMTUuIENocmlzdG9waGVyIER1Qm9pcyBhbmQgRGlyayBFZGRlbGJ1ZXR0ZWwuICoqKkhvdyB0byBzb3J0IGEgZGF0YWZyYW1lIGJ5IGNvbHVtbihzKT8qKiouIFJldHJpZXZlZCBmcm9tIFtodHRwOi8vc3RhY2tvdmVyZmxvdy5jb21dKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzEyOTY2NDYvaG93LXRvLXNvcnQtYS1kYXRhZnJhbWUtYnktY29sdW1ucykuDQoNCjE2LiBDaGFuZywgV2luc3Rvbi4gKioqQ29va2Jvb2sgZm9yIFIqKiouIFNlYmFzdG9wb2w6IE8nUmVpbGx5IE1lZGlhLCAyMDEzLiBSZXRyaWV2ZWQgZnJvbSBbaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9dKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vKQ==