<<BACK

The reproducible workflow

My unofficial mantra (again):

Adam Savage via Pintrest

“Adam Savage via Pintrest”

In Data organization with spreadsheets we learned how to write down our data, avoid common errors, and how to save your data so that you and others can read and understand it later.

Now let’s learn how to write down ALL the steps needed to take you from your raw data to publication quality figures. That’s the key to reproducibility, if every step is written down, then others can reproduce your findings! Below, You will learn how to clean your data in a reproducible way using OpenRefine and R.

Pro-tip

After spending all that time entering your raw data, NEVER change it again.

  • Do not edit the data
  • Do not edit the column headers
  • Do not remove ‘outliers’
  • Do not do calculations directly on the raw data

Store your data in a raw_data directory (folder) in your project directory, and never save/write over it!

I advise you archive your data immediately upon collection to reduce risk of data loss. Zenodo and Figshare both offer different free options for archiving your raw data permanently and have some awesome options for embargoes, restricted access, or even private storage to suite your privacy needs. More detail can be found in the Data Archiving & version controls lesson.

By now I’ve probably said the words reproducible and reproducibility so often that it’s starting to lose meaning. Trust me, this is important! (or don’t trust me and read “A manifesto for reproducible Science”, and “Reproducible Data Science with R”). By not ‘hiding’ you workflow, you help:

  • yourself in the future since:
    • you can easily re-run your analysis after ‘reviewer 3’ makes you add one data point
    • you will be able to easily adapt your method
    • you will receive higher citations since someone may cite you for your methods/data
    • you will appear like an honest/better scientist
  • other scientists since:
    • they will be able to reuse/adapt your methods/data
    • they will be better able to judge your methods as appropriate (for your data during review, or their data in the future)
  • ‘Science’ since
    • discoveries are published more efficiently
    • findings are ‘correct’ more often
    • there is increased trust in ‘Science’, which we badly need right now

OpenRefine

OpenRefine (formerly Google Refine) is a powerful tool for working with messy data: cleaning it; transforming it from one format into another. It’s effectively a reproducible way to work in a spreadsheet, no coding is required on your part since it generates a script that details what you did to the data, step by step.

First, let’s open OpenRefine. You’ll notice it opens in the browser, but it’s running locally (does not require internet connection).

The first step is to load your data and to create a project. If you haven’t done so already, please download the zip file the entire github project repository (contains data files, and all the R scripts used to make this very website!) and extract it somewhere convenient.

In OpenRefine, click Choose Files and find the larval abundance.csv which is in the rawdata directory of the project folder. Then click on the Create Project button (you may want to rename the project).

Data Cleaning

You are now working on a copy of the raw data and changes you make in OpenRefine will not ‘break’ your original raw data. In here you can do all the regular ‘spreadsheet-y’ things. You can edit specific cells, sort, undo/redo, view subsets of your data (facet), etc. But the more powerful functions of OpenRefine are:

  • Cluster (click on column header arrow, then Edit cells > Cluster and Edit...) which means “finding groups of different values that might be alternative representations of the same thing”. For example, the two strings “New York” and “new york” are very likely to refer to the same concept and just have capitalization differences.

  • Whitespace management (click on column header arrow, then Edit cells > Common transforms > Trim leading and trailing whitespace. and Edit cells > Common transforms > Collapse consecutive whitespace.) Strings with spaces at the beginning or end are particularly hard for we humans to tell from strings without, but the blank characters will make a difference to the computer. We usually want to remove these.

Reproducibility

OpenRefine saves every change, every edit you make to the larvalAbundance in a file you can save on your machine. If you had 20 files to clean, and they all had the same type of errors, and all files had the same columns, you could save the script, open a new file to clean, paste in the script and run it. Voila, clean data.

  • In the Undo / Redo section, click Extract, save the bits desired using the check boxes.
  • Copy the code and paste it into a text editor. Save it as a .txt file.
  • To run these steps on a new larvalAbundance, import the new larvalAbundance into OpenRefine, open the Extract / Apply section, paste in the .txt file, click Apply.

For more information and tutorials on OpenRefine, please see Data Carpentry

R and RStudio

You can do everything mentioned above in R, it may at first appear more difficult to do it in R, but in my opinion, you will save time by streamlining your workflow using just one tool. I’m not at all disouraging the use of OpenRefine,it is open source and reproducible, such much kudos is due.

For this and future lessons, we will focus on achieving reproducibility by using R which is an open source language and environment for statistical computing and graphics. There are many other such languages (e.g. Python, Julia, MATLAB,etc) used by conservationists, biologist, and oceanographers; however, we believe R is currently the most widely adopted among our colleagues and also has the most convenient set of statistical tools developed for our field.

Intro to R

In “the olden days” we had to walk to school uphill both ways used R in the terminal or using the built in graphical use interface (GUI). Yes, before 2011, RStudio did not exist and yes, R and RStudio are not the same thing!

R is accessible in the terminal (that thing that looks like DOS, and in case my ‘old’ is showing, the thing that is usually a black screen, a blinking cursor and you can only type in commands) by typing R.exe on Windows, or just R on Mac or Linux. In this way, you can type commands in one by one, or similar to what we just saw with OpenRefine, you save your steps/instuctions/commands in a plain text file (with a .R extension instead of .txt) and you can run those in the terminal by typing Rscript.exe scriptname.R on Windows, or just Rscript scriptname.R on Mac or Linux. While I don’t often work in this way anymore, but this is the only option when using Compute Canada’s awesome resources. Using the commands above and a little server specific magic, you can run your scripts on 100’s of processors instead of the one lonely processor on your computer! I’ve used hundreds of years of computer time in a matter of weeks, all for free! If you are affiliated with any Canadian university, you can do this too!

This is what plain vanilla R looks like

“This is what ‘plain vanilla’ R looks like”

R also come with it’s own GUI, in which you can have a script editor, which is essentially a plain text editor, to write/develop your script and an interactive R console where you can actually execute commands. The advantage of the GUI is that you can execute the entire script (‘source’) or run it line by line all while recording your commands in the script file.

Intro to RStudio

RStudio takes this GUI concept a bit further and provides you with several extra support window. If the idea of having windows for your environment, your files, your plots, as well as packages and a help tab all at hand does not excite you, hold on tight, you’ll get there.

There’s also a lot more information about RStudio on their cheatsheet. On the subject of cheatsheets, RStudio has developed several super useful cheatsheets; seriously, you probably will want to print most of these and put them on the wall in your office.

Enough talk! Let’s get coding! The Fundamentals

You read my mind! However, before we get to cleaning the data, we need to cover a few R fundamentals so that what we do in later steps makes sense.

Go back to the project folder you downloaded during the OpenRefine lesson and open the 2017-CHONe-Data.Rproj file. This is an R project file that allows you to set a number of options for the project (see here), but for our purposes just know that the project file is setting the ‘working directory’, it tells R where all your files are. We’ll get back to that later.

In R you can do math, type the command below in your R console and hit enter:

1+1

You can also assign values to variables, or in R parlance an ‘object’ using the <- symbol (shortkey Alt + -).

a <- 2
b <- 1+2

You’ll notice that there was no output this time there was no output. That’s because the value on the right side of the <- symbol is assigned to a object, it goes to your environment (top right window) instead of being output to the console. In the 1+1 example above, there was no object to go to, so it defaulted to printing in the console.

You can see the contents of a object in the environment window, or by typing the object into the console. You can also use these objects like algebra

a
b
a/b

Up to now, we’ve been dealing with numbers, but R can also deal with character string if surrounded by single or double quotation marks. According to R help (I learned this today!): “Single and double quotes delimit character constants. They can be used interchangeably but double quotes are preferred (and character constants are printed using double quotes), so single quotes are normally only used to delimit character constants containing double quotes.”

f <- "This is a character string, you can tell because of the quotation marks"

A object can also contain multiple values, this is called a vector. The : symbol essentially means ‘to’

x <- 1:3

Another way to do that, with more flexibility is using the c(); the c is short for concatenate and the round brackets indicate that it’s a function. So this concatenate function will concatenate all the ‘arguments’ (things inside the round brackets) which are separated by commas. You can also combine these strategies

x <- c(1,3,5)
y <- c(1:4,6,8)

There are many functions, but they all follow the format functionName(argument1,argument2,argument3,...) where the ‘arguments’ are the input to the function. Some are fairly straightforward:

mean(y)

But even then there are some surprises, let’s look at the help file for mean(). To do that you can: - if you are on the active line in the console or anywhere in a script, put your cursor on the function and press F1 - in the console, type ?mean (or ??mean if your not so sure mean is the name of the function) - find the help window (one of the tabs for the bottom right window) and use the search bar - also, when all else fails, Google is your friend!

Any method should get you to something like this: In R in most cases you could use = instead of the <- symbol with no problems when you are assigning a value to a object. However, it is best practice to use <- when assigning environment objects and = when defining function arguments. Oh, and NA in R means ‘Not Available’ / Missing Values. Like so:

x <- c(1,2,5,7,88,3,4,2,4,6,7,NA)

mean(x)

mean(x, na.rm = TRUE)

I also snuck a TRUE in there; TRUE and FALSE are called logical and are distinct from numeric or character strings. They are sometimes used as arguments values, but they can also used to test things. The == asks if both sides are equal (since the single = is already used for other things), and the != asks if both sides are not equal.

2==1
2==2
2!=1

Creating your own scripts

Up until now, we’ve been playing in the console which means the ‘instructions’ we need to save to reproduce our science are lost (well not really, they can be retrieved from the History tab in the top right, or the console if it hasn’t rolled off the screen). It is a good idea to develop your analysis using a script file (those simple text files with the .R extension I was talking about earlier) because you can save your code easily.

To create a new script, your can click on the little paper with the plus symbol (see below), or you can hit Ctrl+Shift+N (Windows), or Command+Shift+N (Mac), and if that’s not enough options, you can click File > New File > New Script

These scripts are designed to read by R from top to bottom when you hit the button, or Ctrl+Shift+S (Windows), or Command+Shift+S (Mac). Alternatively, you can run portions of your code with Ctrl+Enter (Windows), or Command+Enter (Mac) and either putting your cursor on a line to run the entire line, or highlighting a subsection of code to run just that portion. This will allow us to build multi-step data processing and analysis scripts.

Pro-tip If you don’t want R to read something, us the #. Anything that is preceded by a # is regarded as a ‘comment’ by R and it does not try to execute those lines (i.e. R ignores anything after a #). This is also useful if you want to avoid running a few lines of code when you are developing your script. Instead of typing a # in front of each line of code, you can highlight the lines you want commented out and hit Ctrl+Shift+C (Windows), or Command+Shift+C (Mac). Magic!

Commenting is super useful to include human readable instructions/documentation in your code. Let’s give this a try, write this chunk of code into your script, then run it line by line.

x <- 1

x <- 2

# x <- 3

What is the value of x after running all the lines and why?

Indexing and dimensions

We already mentioned that we can have multiple values in an object, but so far this has been in only 1 dimension, but using matrix() (2D) and array() (>2D) we can store numbers in multiple dimensions

# make a matrix
x <- matrix(data = c(1,1,2,5,3,4), nrow = 2, ncol = 3)

x

# make an array

y <- array(data = c(1:8), dim = c(2,2,2))

y

Exercise

Try storing numeric and character strings in a matrix (or an array). What happens to the numerics?

That’s great that we can store this data in multiple dimensions, but how do I get it back? That’s what [] are for!

# in 1D
x <- c(1,2,3,4,6,34,2,1,5,6,7)
# if we want the 7th value
x[7]
# if we want the 2nd and 7th value
x[c(2,7)]
# if we want only values greater than 10
x[x>10]
# whoa, that blew my mind! How did that work?
# well indexing works by giving the numeric index, or a logical vector the length of the vector we're working with
# so x>10 produces a logical vector the length of x
x>10

# in 2D
x <- matrix(data = c(1,1,2,5,3,4), nrow = 2, ncol = 3)
# this works similar, but you need to provide 2 numbers (or 2 vectors of numbers), for row number and for column number
x[1,3]
x[c(1,2),1]
# if you leave one dimension blank, the whole row or column will be returned
x[,1]
x[2,]

# in 3D, add another dimension!
x <- array(data = c(1:24), dim = c(2,3,4))
x[1,2,3]
x[,,3]

Data frames (data.frame()) are a special type of matrix that can hold numeric and character strings (and logicals, factors, geometries, etc) without having to convert them all to a single type. It’s effectively a collection of vectors of the same length.

# make a data frame
x <- data.frame(nums = c(1,2,3),
                chars = c("one","two","three"),
                logis = c(TRUE, FALSE, TRUE))

# print the whole thing to the console
x

# or for a very big dataframe, you may want to use head to see the top 5 rows
head(x)

# use the str function to see it's structure
str(x)

Pro-tip

Did you notice that the structure of chars was Factor and not chr? Factors are a special type of of character string vector that conserves information about the vector’s ‘levels’ (and you can also order those levels). Many functions, such as data.frame() have an argument called stringsAsFactors and is usually set to TRUE by default, you may want to set this to FALSE. Often, these are interchangeable, but I have had many frustrating errors because I mistakenly had a character string where I needed a factor and vice versa. Be aware that they are different and not knowing which one you have can lead to errors. You can easily convert back and forth with as.factor (or as.ordered()) and as.character()

# let's try having both strings and factors in the data frame
# make a data frame
x <- data.frame(nums = c(1,2,3),
                chars = c("one","two","three"),
                facts = as.factor(c("one","two","three")),
                logis = c(TRUE, FALSE, TRUE),
                stringsAsFactors = FALSE)

# print the whole thing to the console
x

# or for a very big dataframe, you may want to use head to see the top 5 rows
head(x)

# use the str function to see it's structure
str(x)

Indexing in dataframes can be the same as for a 2D matrix, or you can use $ to access columns by name

# make a data frame
x <- data.frame(nums = c(1,2,3),
                chars = c("one","two","three"),
                facts = as.factor(c("one","two","three")),
                logis = c(TRUE, FALSE, TRUE),
                stringsAsFactors = FALSE)

x[1,2]
x[,2]
x$chars

# and you can also treat these columns as 1D vectors
x$chars[1]

Reading in data

That’s all great, but our data is store in a .csv file, how do we get that? The simplest way is to use the read.csv() function. But we need to know where on the computer the file is stored.

The first step is to load your data and to create a project. If you haven’t done so already, please download the zip file the entire github project repository (contains data files, and all the R scripts used to make this very website!) and extract it somewhere convenient.

Go back to the project folder you extracted and open the 2017-CHONe-Data.Rproj file. This is an R project file that allows you to set a number of options for the project (see here), but for our purposes just know that the project file is setting the ‘working directory’, it tells R where all your files are.

# you can hard code the whole file path, but try to never do that!
# your computer may not have a C drive, and your name almost certainly is not Remi, so this will not work for you

# larvalAbundance <- read.csv("C:/Users/Remi-Work/Desktop/2017-CHONe-Data/rawdata/larval abundance.csv")

# The above is unsurprisingly not reproducible! Always use relative paths!
# Relative paths are a shortened version of the above, you only need to type what comes after the project directory
# REMINDER: The project directory is where the .Rproj file is stored

larvalAbundance <- read.csv("rawdata/larval abundance.csv", stringsAsFactors = FALSE)

# writing it this way (relative path) means that your project is reproducible since you can move this whole directory to another location on your computer OR ANY OTHER COMPUTER!!!

This data comes from one of Remi’s PhD thesis chapters1 that was part of the first CHONe. I modified it from the original version archived on Dryad so we would have cleaning to do!

Cleaning data in R

Common errors in data are:

  • trailing or leading white spaces in character strings
  • data entered in different formats (e.g. numbers and character strings in one column)
  • data entered with wrong units

The first step I always take is to make sure that the data loaded in as expected. Head over to the Environment window and click on data. You can do (temporary) sorting and filtering of the data in the data viewer. I also use the str() function to make sure all the variables (columns) are of the correct type.

str(larvalAbundance)

If I wanted to make a correction manually, we can use indexing:

# say for example, I knew that the second observation was in fact taken at 12 m depth
larvalAbundance$depth[2]

# the value in the data frame is indeed 3 WRONG! Let,s correct it
larvalAbundance$depth[2] <- 12
head(larvalAbundance)

# The other issue you may notice is that my months values are mostly numbers, but there's at least 1 "August" in there
# We could correct just that one we see on line 4
larvalAbundance$month[4] <- 8

# Or we could get rid of all the "August"'s in one pass
larvalAbundance$month[larvalAbundance$month=="August"] <- 8

# but that column is still a character string, let's convert it to numeric
larvalAbundance$month <- as.numeric(larvalAbundance$month)

str(larvalAbundance)

If you were wondering how many years I had sampled, you could do:

unique(larvalAbundance$year)

# or for time
unique(larvalAbundance$time)

# as you can see there are a few trailing white spaces, to do text substitutions, let's use gsub()
# but first let's see how this works; it matches a pattern in x and replaces it.

gsub(pattern = "ABC",replacement = "XYZ",x = "TUVWABC")

gsub(pattern = "doesn't work",replacement = "works",x = "If this sentence no longer contains the pattern, then gsub doesn't work")

# So now, let's use gsub to remove those spaces. The patter we are matching is just a space and the replacement is nothing, so that will remove white spaces

larvalAbundance$time <- gsub(pattern = " ",replacement = "",x = larvalAbundance$time)

Feel the power! The gsub() function is very powerful and the pattern matching works based on ‘regular expressions’ (which is a nearly universal pattern matching language/protocol). For example, if you had spaces you wanted to keep and only wanted to remove white spaces at the end, you could use pattern = " +$" since the dollar sign in regex means ‘ends with’ and the plus means ‘one or more’, so gsub would match one or more spaces at the end of a character string. You can practice your ‘regex’ with regexpal and this cheatsheet

Other useful things you should check are the minimum and maximum values for each column to make sure things were entered all in the same order of magnitude, or a even a quick histogram

min(larvalAbundance$Margarites.spp.)
max(larvalAbundance$Margarites.spp.)
hist(larvalAbundance$Margarites.spp.)

Lastly, there are some column names that are not ‘up to code’, so to avoid a stern talking to from CHONe’s data manager, let’s fix that now! (Also, you may have noticed that latitude and longitude were swapped!)

names(larvalAbundance)

names(larvalAbundance)[names(larvalAbundance)=="long"] <- "decimalLatitude"
names(larvalAbundance)[names(larvalAbundance)=="lat"] <- "decimalLongitude"
names(larvalAbundance)[names(larvalAbundance)=="site"] <- "locationID"

Everything now seems reasonable to me, but it is your responsibility to check that each column of your data ‘makes sense’. But for sake of time, lets move on!

Now we have a choice, we can either run a data cleaning script every time we load the raw data, or we can save a ‘clean’ data product. Let’s do the latter.

# let's create a new folder for intermediate data products
dir.create("data")

# Then let's save the cleaned data in that folder
write.csv(larvalAbundance, file = "data/larvalAbundanceClean.csv", row.names = FALSE)

Pro-tip

Notice that: - we are not writing over the raw data - we are not writing in the same folder as the raw data - we are naming our new data file informatively

Making your own functions and packages

Part of R’s awesomeness is that it already comes with a lot of functions that are very useful for everyday science. Additionally, there are many packages which are essentially collections of new functions and help files generated by users like you and me that add to the already broad functionality of R.

Warning: shameless self promotion below!

Here is a package I created called BESTMPA and the peer-reviewed paper describing it: An adaptable toolkit to assess commercial fishery costs and benefits related to marine protected area network design

Making your own function follows the particular format below, let’s make one called custommean()


custommean <- function(x){
    m <- sum(x)/length(x)
    return(m)
}

# so we defined custommean as a function with arguments 'x', and it does what is inside the curly brackets
# it will return m which is the mean of x.

x <- c(1,2,3,6)

# does it work?
custommean(x = x)

If you make a few of those and write some help files to go along with them, you can make your own package. If you’re interested see the “R packages” book by Hadley Wickham (Chief Scientist at RStudio, not the last time I will mention him), but making packages is beyond the scope of what I can cover in this workshop

Anyway, there is a central organization called ‘Comprehensive R Archive Network’ or CRAN which houses all the official package, but there are also other packages (like mine), as well as the development version of many of the official packages on github that are worth taking a look at.

Install packages for tomorrow

To install packages, you can either use the Packages window at the bottom right, or you can do it with written commands (my preference). Here are a few we will use tomorrow, try installing them now and let us know if you get any errors.

install.packages('tidyverse') # The tidyverse is a collection of R packages that share common philosophies and are designed to work together. (e.g. ggplot2, dplyr, tidyr)

install.packages('marmap') # Import xyz data from the NOAA (National Oceanic and Atmospheric Administration, <http://www.noaa.gov>), GEBCO (General Bathymetric Chart of the Oceans, <http://www.gebco.net>) and other sources, plot xyz data to prepare publication-ready figures

install.packages('raster') # Reading, writing, manipulating, analyzing and modeling of gridded spatial data (and also getting access to GADM basemaps)

install.packages('devtools') # Allows you to install packages from github

# the 'robis' package on CRAN does not work with the latest version of R (yet), so we need to get the latest version from github
devtools::install_github("iobis/robis")

# you already have the CRAN version of ggplot2, but that version is not compatible with the sf package yet
# So, we need the github version of ggplot2 as well!
devtools::install_github("tidyverse/ggplot2")

install.packages('gridExtra') # to be able to arrange multiple ggplot plots

install.packages('taxize') # To extract and validate species taxonomy

install.packages('rfishbase') # To access resources available on Fishbase and SeaLifeBase

install.packages('rglobi') # To access interactions data

devtools::install_github("ropensci/rnoaa") # To access environmental data from the NOAA databases

install.packages('knitr')

install.packages('biomod2') # to perform species distribution models

install.packages('iGraph') # to produce network plots

install.packages('networkD3') # to produce html (a.k.a. interactive) network plots!

devtools::install_github("guiblanchet/HMSC") # package to perform hierarchical modeling of species communities

install.packages('coda') # package to summarize and plot outputs from Markov Chains

install.packages('corrplot') # visualization of correlation matrices

install.packages('circlize') # circular visualization of data

install.packages('ModelMetrics') # collection of metrics coded for efficiency in C++ using Rcpp

install.packages("pdftools") # to extract pdf content

install.packages('stringr') # Simple, Consistent Wrappers for Common String Operations

install.packages('tidytext') # text analysis package

install.packages('viridis') # Port of the new 'matplotlib' color maps

install.packages('tibble') # Simple Data Frames

devtools::install_github("dgrtwo/widyr") # Widen, process, and re-tidy a dataset

install.packages('ggraph') # An Implementation of Grammar of Graphics for Graphs and Networks

install.packages('wordcloud2') # wordle generator

install.packages('leaflet') # Create and customize interactive maps

install.packages('mapview') # Interactive Viewing of Spatial Objects in R

install.packages('scales') # Graphical scales map data to aesthetics


# to gain access to the functions in a package, you can use library(), eg:
library(tidyverse)

Keeping R up to date

Every so often, R releases a new update. Many of you had install issues last night that were resolved by installing the newest version of R. To keep things running smoothly in the future, here is a great trick:

# install the package called installr
install.packages("installr")

# load the library
library(installr)

# run the updater function (this is best done outside Rstudio in the R gui)
updater()

This will prompt you to install the latest version of R and copy over all of you packages if you choose to do so (the alternative is installing them all by hand again), and it can also update your packages for you. It’s really a great time saver!

<<BACK


  1. Daigle RM, Metaxas A, deYoung B (2014) Bay-scale patterns in the distribution, aggregation and spatial variability of larvae of benthic invertebrates. Marine Ecology Progress Series 503:139-156. http://dx.doi.org/10.3354/meps10734

LS0tDQp0aXRsZTogIkRhdGEgY2xlYW5pbmcgYW5kIHJhdyBkYXRhIG1hbmFnZW1lbnQiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCi0tLQ0KWzw8QkFDS10oaHR0cHM6Ly9yZW1pLWRhaWdsZS5naXRodWIuaW8vMjAxNy1DSE9OZS1EYXRhLykNCg0KIyBUaGUgcmVwcm9kdWNpYmxlIHdvcmtmbG93DQoNCk15IHVub2ZmaWNpYWwgbWFudHJhIChhZ2Fpbik6DQoNCiFbIkFkYW0gU2F2YWdlIHZpYSBQaW50cmVzdCJdKGh0dHBzOi8vcy1tZWRpYS1jYWNoZS1hazAucGluaW1nLmNvbS81NjR4L2U5L2YyLzE5L2U5ZjIxOWRjZTMwZjEzNjcwMTU4ODMyMzEwYjBlNDJjLmpwZykNCg0KSW4gIFtEYXRhIG9yZ2FuaXphdGlvbiB3aXRoIHNwcmVhZHNoZWV0c10oaHR0cHM6Ly9yZW1pLWRhaWdsZS5naXRodWIuaW8vMjAxNy1DSE9OZS1EYXRhL29yZ2FuaXphdGlvbi5uYi5odG1sKSB3ZSBsZWFybmVkIGhvdyB0byB3cml0ZSBkb3duIG91ciBkYXRhLCBhdm9pZCBjb21tb24gZXJyb3JzLCBhbmQgaG93IHRvIHNhdmUgeW91ciBkYXRhIHNvIHRoYXQgeW91IGFuZCBvdGhlcnMgY2FuIHJlYWQgYW5kIHVuZGVyc3RhbmQgaXQgbGF0ZXIuDQoNCk5vdyBsZXQncyBsZWFybiBob3cgdG8gd3JpdGUgZG93biAqKkFMTCoqIHRoZSBzdGVwcyBuZWVkZWQgdG8gdGFrZSB5b3UgZnJvbSB5b3VyIHJhdyBkYXRhIHRvIHB1YmxpY2F0aW9uIHF1YWxpdHkgZmlndXJlcy4gVGhhdCdzIHRoZSBrZXkgdG8gcmVwcm9kdWNpYmlsaXR5LCBpZiBldmVyeSBzdGVwIGlzIHdyaXR0ZW4gZG93biwgdGhlbiBvdGhlcnMgY2FuIHJlcHJvZHVjZSB5b3VyIGZpbmRpbmdzISBCZWxvdywgWW91IHdpbGwgbGVhcm4gaG93IHRvIGNsZWFuIHlvdXIgZGF0YSBpbiBhIHJlcHJvZHVjaWJsZSB3YXkgdXNpbmcgT3BlblJlZmluZSBhbmQgUi4NCg0KPiAqKlByby10aXAqKg0KPg0KPiBBZnRlciBzcGVuZGluZyBhbGwgdGhhdCB0aW1lIGVudGVyaW5nIHlvdXIgcmF3IGRhdGEsICoqTkVWRVIqKiBjaGFuZ2UgaXQgYWdhaW4uDQo+DQo+IC0gRG8gbm90IGVkaXQgdGhlIGRhdGENCj4gLSBEbyBub3QgZWRpdCB0aGUgY29sdW1uIGhlYWRlcnMNCj4gLSBEbyBub3QgcmVtb3ZlICdvdXRsaWVycycNCj4gLSBEbyBub3QgZG8gY2FsY3VsYXRpb25zIGRpcmVjdGx5IG9uIHRoZSByYXcgZGF0YQ0KPg0KPiBTdG9yZSB5b3VyIGRhdGEgaW4gYSBgcmF3X2RhdGFgIGRpcmVjdG9yeSAoZm9sZGVyKSBpbiB5b3VyIHByb2plY3QgZGlyZWN0b3J5LCBhbmQgbmV2ZXIgc2F2ZS93cml0ZSBvdmVyIGl0IQ0KDQpJIGFkdmlzZSB5b3UgYXJjaGl2ZSB5b3VyIGRhdGEgaW1tZWRpYXRlbHkgdXBvbiBjb2xsZWN0aW9uIHRvIHJlZHVjZSByaXNrIG9mIGRhdGEgbG9zcy4gW1plbm9kb10oaHR0cHM6Ly96ZW5vZG8ub3JnLykgYW5kIFtGaWdzaGFyZV0oaHR0cHM6Ly9maWdzaGFyZS5jb20vKSBib3RoIG9mZmVyIGRpZmZlcmVudCAqKmZyZWUqKiBvcHRpb25zIGZvciBhcmNoaXZpbmcgeW91ciByYXcgZGF0YSAqKnBlcm1hbmVudGx5KiogYW5kIGhhdmUgc29tZSBhd2Vzb21lIG9wdGlvbnMgZm9yIGVtYmFyZ29lcywgcmVzdHJpY3RlZCBhY2Nlc3MsIG9yIGV2ZW4gcHJpdmF0ZSBzdG9yYWdlIHRvIHN1aXRlIHlvdXIgcHJpdmFjeSBuZWVkcy4gTW9yZSBkZXRhaWwgY2FuIGJlIGZvdW5kIGluIHRoZSBbRGF0YSBBcmNoaXZpbmcgJiB2ZXJzaW9uIGNvbnRyb2xzXShodHRwczovL3JlbWktZGFpZ2xlLmdpdGh1Yi5pby8yMDE3LUNIT05lLURhdGEvdmVyc2lvbmNvbnRyb2wubmIuaHRtbCkgbGVzc29uLg0KDQohW1tdKGh0dHBzOi8vdHdpdHRlci5jb20vVHJldm9yQUJyYW5jaC9zdGF0dXMvNDY2NzgwODI3OTg1MDAyNDk2KV0ocGljdHVyZXMvQnJhbmNoLnBuZykNCg0KQnkgbm93IEkndmUgcHJvYmFibHkgc2FpZCB0aGUgd29yZHMgcmVwcm9kdWNpYmxlIGFuZCByZXByb2R1Y2liaWxpdHkgc28gb2Z0ZW4gdGhhdCBpdCdzIHN0YXJ0aW5nIHRvIGxvc2UgbWVhbmluZy4gVHJ1c3QgbWUsIHRoaXMgaXMgaW1wb3J0YW50ISAob3IgZG9uJ3QgdHJ1c3QgbWUgYW5kIHJlYWQgWyJBIG1hbmlmZXN0byBmb3IgcmVwcm9kdWNpYmxlIFNjaWVuY2UiXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU2Mi0wMTYtMDAyMSksIGFuZCBbIlJlcHJvZHVjaWJsZSBEYXRhIFNjaWVuY2Ugd2l0aCBSIl0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vcmVwcm9kdWNpYmxlLWRhdGEtc2NpZW5jZS13aXRoLXIvKSkuIEJ5IG5vdCAnaGlkaW5nJyB5b3Ugd29ya2Zsb3csIHlvdSBoZWxwOg0KDQotICoqeW91cnNlbGYqKiBpbiB0aGUgZnV0dXJlIHNpbmNlOg0KICAgIC0geW91IGNhbiBlYXNpbHkgcmUtcnVuIHlvdXIgYW5hbHlzaXMgYWZ0ZXIgJ3Jldmlld2VyIDMnIG1ha2VzIHlvdSBhZGQgb25lIGRhdGEgcG9pbnQNCiAgICAtIHlvdSB3aWxsIGJlIGFibGUgdG8gZWFzaWx5IGFkYXB0IHlvdXIgbWV0aG9kDQogICAgLSB5b3Ugd2lsbCByZWNlaXZlIGhpZ2hlciBjaXRhdGlvbnMgc2luY2Ugc29tZW9uZSBtYXkgY2l0ZSB5b3UgZm9yIHlvdXIgbWV0aG9kcy9kYXRhDQogICAgLSB5b3Ugd2lsbCBhcHBlYXIgbGlrZSBhbiBob25lc3QvYmV0dGVyIHNjaWVudGlzdA0KLSAqKm90aGVyIHNjaWVudGlzdHMqKiBzaW5jZToNCiAgICAtIHRoZXkgd2lsbCBiZSBhYmxlIHRvIHJldXNlL2FkYXB0IHlvdXIgbWV0aG9kcy9kYXRhDQogICAgLSB0aGV5IHdpbGwgYmUgYmV0dGVyIGFibGUgdG8ganVkZ2UgeW91ciBtZXRob2RzIGFzIGFwcHJvcHJpYXRlIChmb3IgeW91ciBkYXRhIGR1cmluZyByZXZpZXcsIG9yIHRoZWlyIGRhdGEgaW4gdGhlIGZ1dHVyZSkNCi0gKionU2NpZW5jZScqKiBzaW5jZQ0KICAgIC0gZGlzY292ZXJpZXMgYXJlIHB1Ymxpc2hlZCBtb3JlIGVmZmljaWVudGx5DQogICAgLSBmaW5kaW5ncyBhcmUgJ2NvcnJlY3QnIG1vcmUgb2Z0ZW4NCiAgICAtIHRoZXJlIGlzIGluY3JlYXNlZCB0cnVzdCBpbiAnU2NpZW5jZScsIHdoaWNoIHdlIGJhZGx5IG5lZWQgcmlnaHQgbm93DQoNCg0KIyBPcGVuUmVmaW5lDQoNCk9wZW5SZWZpbmUgKGZvcm1lcmx5IEdvb2dsZSBSZWZpbmUpIGlzIGEgcG93ZXJmdWwgdG9vbCBmb3Igd29ya2luZyB3aXRoIG1lc3N5IGRhdGE6IGNsZWFuaW5nIGl0OyB0cmFuc2Zvcm1pbmcgaXQgZnJvbSBvbmUgZm9ybWF0IGludG8gYW5vdGhlci4gSXQncyBlZmZlY3RpdmVseSBhIHJlcHJvZHVjaWJsZSB3YXkgdG8gd29yayBpbiBhIHNwcmVhZHNoZWV0LCBubyBjb2RpbmcgaXMgcmVxdWlyZWQgb24geW91ciBwYXJ0IHNpbmNlIGl0IGdlbmVyYXRlcyBhIHNjcmlwdCB0aGF0IGRldGFpbHMgd2hhdCB5b3UgZGlkIHRvIHRoZSBkYXRhLCBzdGVwIGJ5IHN0ZXAuDQoNCkZpcnN0LCBsZXQncyBvcGVuIE9wZW5SZWZpbmUuIFlvdSdsbCBub3RpY2UgaXQgb3BlbnMgaW4gdGhlIGJyb3dzZXIsIGJ1dCBpdCdzIHJ1bm5pbmcgbG9jYWxseSAoZG9lcyBub3QgcmVxdWlyZSBpbnRlcm5ldCBjb25uZWN0aW9uKS4NCiFbXShwaWN0dXJlcy9PUi5wbmcpDQoNClRoZSBmaXJzdCBzdGVwIGlzIHRvIGxvYWQgeW91ciBkYXRhIGFuZCB0byBjcmVhdGUgYSBwcm9qZWN0LiBJZiB5b3UgaGF2ZW4ndCBkb25lIHNvIGFscmVhZHksIHBsZWFzZSBkb3dubG9hZCB0aGUgW3ppcCBmaWxlXShodHRwczovL2dpdGh1Yi5jb20vcmVtaS1kYWlnbGUvMjAxNy1DSE9OZS1EYXRhL2FyY2hpdmUvZ2gtcGFnZXMuemlwKSB0aGUgZW50aXJlICBnaXRodWIgW3Byb2plY3QgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL3JlbWktZGFpZ2xlLzIwMTctQ0hPTmUtRGF0YS8pIChjb250YWlucyBkYXRhIGZpbGVzLCBhbmQgYWxsIHRoZSBSIHNjcmlwdHMgdXNlZCB0byBtYWtlIHRoaXMgdmVyeSB3ZWJzaXRlISkgYW5kIGV4dHJhY3QgaXQgc29tZXdoZXJlIGNvbnZlbmllbnQuDQoNCkluIE9wZW5SZWZpbmUsIGNsaWNrIGBDaG9vc2UgRmlsZXNgIGFuZCBmaW5kIHRoZSBgbGFydmFsIGFidW5kYW5jZS5jc3ZgIHdoaWNoIGlzIGluIHRoZSBgcmF3ZGF0YWAgZGlyZWN0b3J5IG9mIHRoZSBwcm9qZWN0IGZvbGRlci4gVGhlbiBjbGljayBvbiB0aGUgYENyZWF0ZSBQcm9qZWN0YCBidXR0b24gKHlvdSBtYXkgd2FudCB0byByZW5hbWUgdGhlIHByb2plY3QpLg0KDQojIyBEYXRhIENsZWFuaW5nDQpZb3UgYXJlIG5vdyB3b3JraW5nIG9uIGEgY29weSBvZiB0aGUgcmF3IGRhdGEgYW5kIGNoYW5nZXMgeW91IG1ha2UgaW4gT3BlblJlZmluZSB3aWxsIG5vdCAnYnJlYWsnIHlvdXIgb3JpZ2luYWwgcmF3IGRhdGEuIEluIGhlcmUgeW91IGNhbiBkbyBhbGwgdGhlIHJlZ3VsYXIgJ3NwcmVhZHNoZWV0LXknIHRoaW5ncy4gWW91IGNhbiBlZGl0IHNwZWNpZmljIGNlbGxzLCBzb3J0LCB1bmRvL3JlZG8sIHZpZXcgc3Vic2V0cyBvZiB5b3VyIGRhdGEgKGZhY2V0KSwgZXRjLiBCdXQgdGhlIG1vcmUgcG93ZXJmdWwgZnVuY3Rpb25zIG9mIE9wZW5SZWZpbmUgYXJlOg0KDQotICoqQ2x1c3RlcioqIChjbGljayBvbiBjb2x1bW4gaGVhZGVyIGFycm93LCB0aGVuIGBFZGl0IGNlbGxzID4gQ2x1c3RlciBhbmQgRWRpdC4uLmApIHdoaWNoIG1lYW5zIOKAnGZpbmRpbmcgZ3JvdXBzIG9mIGRpZmZlcmVudCB2YWx1ZXMgdGhhdCBtaWdodCBiZSBhbHRlcm5hdGl2ZSByZXByZXNlbnRhdGlvbnMgb2YgdGhlIHNhbWUgdGhpbmfigJ0uIEZvciBleGFtcGxlLCB0aGUgdHdvIHN0cmluZ3Mg4oCcTmV3IFlvcmvigJ0gYW5kIOKAnG5ldyB5b3Jr4oCdIGFyZSB2ZXJ5IGxpa2VseSB0byByZWZlciB0byB0aGUgc2FtZSBjb25jZXB0IGFuZCBqdXN0IGhhdmUgY2FwaXRhbGl6YXRpb24gZGlmZmVyZW5jZXMuDQoNCi0gKipXaGl0ZXNwYWNlIG1hbmFnZW1lbnQqKiAoY2xpY2sgb24gY29sdW1uIGhlYWRlciBhcnJvdywgdGhlbiBgRWRpdCBjZWxscyA+IENvbW1vbiB0cmFuc2Zvcm1zID4gVHJpbSBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLmAgYW5kIGBFZGl0IGNlbGxzID4gQ29tbW9uIHRyYW5zZm9ybXMgPiBDb2xsYXBzZSBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlLmApIFN0cmluZ3Mgd2l0aCBzcGFjZXMgYXQgdGhlIGJlZ2lubmluZyBvciBlbmQgYXJlIHBhcnRpY3VsYXJseSBoYXJkIGZvciB3ZSBodW1hbnMgdG8gdGVsbCBmcm9tIHN0cmluZ3Mgd2l0aG91dCwgYnV0IHRoZSBibGFuayBjaGFyYWN0ZXJzIHdpbGwgbWFrZSBhIGRpZmZlcmVuY2UgdG8gdGhlIGNvbXB1dGVyLiBXZSB1c3VhbGx5IHdhbnQgdG8gcmVtb3ZlIHRoZXNlLg0KDQohW10ocGljdHVyZXMvT1J3aGl0ZXNwYWNlLnBuZykNCg0KIyMgUmVwcm9kdWNpYmlsaXR5DQoNCk9wZW5SZWZpbmUgc2F2ZXMgZXZlcnkgY2hhbmdlLCBldmVyeSBlZGl0IHlvdSBtYWtlIHRvIHRoZSBsYXJ2YWxBYnVuZGFuY2UgaW4gYSBmaWxlIHlvdSBjYW4gc2F2ZSBvbiB5b3VyIG1hY2hpbmUuIElmIHlvdSBoYWQgMjAgZmlsZXMgdG8gY2xlYW4sIGFuZCB0aGV5IGFsbCBoYWQgdGhlIHNhbWUgdHlwZSBvZiBlcnJvcnMsIGFuZCBhbGwgZmlsZXMgaGFkIHRoZSBzYW1lIGNvbHVtbnMsIHlvdSBjb3VsZCBzYXZlIHRoZSBzY3JpcHQsIG9wZW4gYSBuZXcgZmlsZSB0byBjbGVhbiwgcGFzdGUgaW4gdGhlIHNjcmlwdCBhbmQgcnVuIGl0LiBWb2lsYSwgY2xlYW4gZGF0YS4NCg0KLSBJbiB0aGUgYFVuZG8gLyBSZWRvIHNlY3Rpb25gLCBjbGljayBgRXh0cmFjdGAsIHNhdmUgdGhlIGJpdHMgZGVzaXJlZCB1c2luZyB0aGUgY2hlY2sgYm94ZXMuDQotIENvcHkgdGhlIGNvZGUgYW5kIHBhc3RlIGl0IGludG8gYSB0ZXh0IGVkaXRvci4gU2F2ZSBpdCBhcyBhIGAudHh0YCBmaWxlLg0KLSBUbyBydW4gdGhlc2Ugc3RlcHMgb24gYSBuZXcgbGFydmFsQWJ1bmRhbmNlLCBpbXBvcnQgdGhlIG5ldyBsYXJ2YWxBYnVuZGFuY2UgaW50byBPcGVuUmVmaW5lLCBvcGVuIHRoZSBgRXh0cmFjdCAvIEFwcGx5YCBzZWN0aW9uLCBwYXN0ZSBpbiB0aGUgYC50eHRgIGZpbGUsIGNsaWNrIGBBcHBseWAuDQoNCkZvciBtb3JlIGluZm9ybWF0aW9uIGFuZCB0dXRvcmlhbHMgb24gT3BlblJlZmluZSwgcGxlYXNlIHNlZSBbRGF0YSBDYXJwZW50cnldKGh0dHA6Ly93d3cuZGF0YWNhcnBlbnRyeS5vcmcvT3BlblJlZmluZS1lY29sb2d5LWxlc3Nvbi8pDQoNCiMgUiBhbmQgUlN0dWRpbw0KDQpZb3UgY2FuIGRvIGV2ZXJ5dGhpbmcgbWVudGlvbmVkIGFib3ZlIGluIFtSXShodHRwOi8vY3Jhbi51dHN0YXQudXRvcm9udG8uY2EvKSwgaXQgbWF5IGF0IGZpcnN0IGFwcGVhciBtb3JlIGRpZmZpY3VsdCB0byBkbyBpdCBpbiBSLCBidXQgaW4gbXkgb3BpbmlvbiwgeW91IHdpbGwgc2F2ZSB0aW1lIGJ5IHN0cmVhbWxpbmluZyB5b3VyIHdvcmtmbG93IHVzaW5nIGp1c3Qgb25lIHRvb2wuIEknbSBub3QgYXQgYWxsIGRpc291cmFnaW5nIHRoZSB1c2Ugb2YgT3BlblJlZmluZSxpdCBpcyBvcGVuIHNvdXJjZSBhbmQgcmVwcm9kdWNpYmxlLCBzdWNoIG11Y2gga3Vkb3MgaXMgZHVlLg0KDQpGb3IgdGhpcyBhbmQgZnV0dXJlIGxlc3NvbnMsIHdlIHdpbGwgZm9jdXMgb24gYWNoaWV2aW5nIHJlcHJvZHVjaWJpbGl0eSBieSB1c2luZyBbUl0oaHR0cDovL2NyYW4udXRzdGF0LnV0b3JvbnRvLmNhLykgd2hpY2ggaXMgYW4gb3BlbiBzb3VyY2UgbGFuZ3VhZ2UgYW5kIGVudmlyb25tZW50IGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcgYW5kIGdyYXBoaWNzLiBUaGVyZSBhcmUgbWFueSBvdGhlciBzdWNoIGxhbmd1YWdlcyAoZS5nLiBbUHl0aG9uXShodHRwczovL3d3dy5weXRob24ub3JnLyksIFtKdWxpYV0oaHR0cHM6Ly9qdWxpYWxhbmcub3JnLyksIFtNQVRMQUJdKGh0dHBzOi8vd3d3Lm1hdGh3b3Jrcy5jb20vcHJvZHVjdHMvbWF0bGFiLmh0bWwpLGV0YykgdXNlZCBieSBjb25zZXJ2YXRpb25pc3RzLCBiaW9sb2dpc3QsIGFuZCBvY2Vhbm9ncmFwaGVyczsgaG93ZXZlciwgd2UgYmVsaWV2ZSBSIGlzIGN1cnJlbnRseSB0aGUgbW9zdCB3aWRlbHkgYWRvcHRlZCBhbW9uZyBvdXIgY29sbGVhZ3VlcyBhbmQgYWxzbyBoYXMgdGhlIG1vc3QgY29udmVuaWVudCBzZXQgb2Ygc3RhdGlzdGljYWwgdG9vbHMgZGV2ZWxvcGVkIGZvciBvdXIgZmllbGQuDQoNCiMjIEludHJvIHRvIFINCg0KSW4gInRoZSBvbGRlbiBkYXlzIiB3ZSB+fmhhZCB0byB3YWxrIHRvIHNjaG9vbCB1cGhpbGwgYm90aCB3YXlzfn4gdXNlZCBSIGluIHRoZSB0ZXJtaW5hbCBvciB1c2luZyB0aGUgYnVpbHQgaW4gZ3JhcGhpY2FsIHVzZSBpbnRlcmZhY2UgKEdVSSkuIFllcywgYmVmb3JlIDIwMTEsIFJTdHVkaW8gZGlkIG5vdCBleGlzdCBhbmQgeWVzLCBSIGFuZCBSU3R1ZGlvIGFyZSBub3QgdGhlIHNhbWUgdGhpbmchDQoNClIgaXMgYWNjZXNzaWJsZSBpbiB0aGUgdGVybWluYWwgKHRoYXQgdGhpbmcgdGhhdCBsb29rcyBsaWtlIERPUywgYW5kIGluIGNhc2UgbXkgJ29sZCcgaXMgc2hvd2luZywgdGhlIHRoaW5nIHRoYXQgaXMgdXN1YWxseSBhIGJsYWNrIHNjcmVlbiwgYSBibGlua2luZyBjdXJzb3IgYW5kIHlvdSBjYW4gb25seSB0eXBlIGluIGNvbW1hbmRzKSBieSB0eXBpbmcgYFIuZXhlYCBvbiBXaW5kb3dzLCBvciBqdXN0IGBSYCBvbiBNYWMgb3IgTGludXguIEluIHRoaXMgd2F5LCB5b3UgY2FuIHR5cGUgY29tbWFuZHMgaW4gb25lIGJ5IG9uZSwgb3Igc2ltaWxhciB0byB3aGF0IHdlIGp1c3Qgc2F3IHdpdGggT3BlblJlZmluZSwgeW91IHNhdmUgeW91ciBzdGVwcy9pbnN0dWN0aW9ucy9jb21tYW5kcyBpbiBhIHBsYWluIHRleHQgZmlsZSAod2l0aCBhIGAuUmAgZXh0ZW5zaW9uIGluc3RlYWQgb2YgYC50eHRgKSBhbmQgeW91IGNhbiBydW4gdGhvc2UgaW4gdGhlIHRlcm1pbmFsIGJ5IHR5cGluZyBgUnNjcmlwdC5leGUgc2NyaXB0bmFtZS5SYCBvbiBXaW5kb3dzLCBvciBqdXN0IGBSc2NyaXB0IHNjcmlwdG5hbWUuUmAgb24gTWFjIG9yIExpbnV4LiBXaGlsZSBJIGRvbid0IG9mdGVuIHdvcmsgaW4gdGhpcyB3YXkgYW55bW9yZSwgYnV0IHRoaXMgaXMgdGhlIG9ubHkgb3B0aW9uIHdoZW4gdXNpbmcgW0NvbXB1dGUgQ2FuYWRhXShodHRwczovL3d3dy5jb21wdXRlY2FuYWRhLmNhL3Jlc2VhcmNoLXBvcnRhbC9uYXRpb25hbC1zZXJ2aWNlcy9jb21wdXRlLykncyBhd2Vzb21lIHJlc291cmNlcy4gVXNpbmcgdGhlIGNvbW1hbmRzIGFib3ZlIGFuZCBhIGxpdHRsZSBzZXJ2ZXIgc3BlY2lmaWMgbWFnaWMsIHlvdSBjYW4gcnVuIHlvdXIgc2NyaXB0cyBvbiAxMDAncyBvZiBwcm9jZXNzb3JzIGluc3RlYWQgb2YgdGhlIG9uZSBsb25lbHkgcHJvY2Vzc29yIG9uIHlvdXIgY29tcHV0ZXIhIEkndmUgdXNlZCBodW5kcmVkcyBvZiB5ZWFycyBvZiBjb21wdXRlciB0aW1lIGluIGEgbWF0dGVyIG9mIHdlZWtzLCBhbGwgZm9yIGZyZWUhIElmIHlvdSBhcmUgYWZmaWxpYXRlZCB3aXRoIGFueSBDYW5hZGlhbiB1bml2ZXJzaXR5LCB5b3UgY2FuIGRvIHRoaXMgdG9vIQ0KDQohWyJUaGlzIGlzIHdoYXQgJ3BsYWluIHZhbmlsbGEnIFIgbG9va3MgbGlrZSJdKHBpY3R1cmVzL1J0ZXJtLnBuZykNCg0KUiBhbHNvIGNvbWUgd2l0aCBpdCdzIG93biBHVUksIGluIHdoaWNoIHlvdSBjYW4gaGF2ZSBhIHNjcmlwdCBlZGl0b3IsIHdoaWNoIGlzIGVzc2VudGlhbGx5IGEgcGxhaW4gdGV4dCBlZGl0b3IsIHRvIHdyaXRlL2RldmVsb3AgeW91ciBzY3JpcHQgYW5kIGFuIGludGVyYWN0aXZlIFIgY29uc29sZSB3aGVyZSB5b3UgY2FuIGFjdHVhbGx5IGV4ZWN1dGUgY29tbWFuZHMuIFRoZSBhZHZhbnRhZ2Ugb2YgdGhlIEdVSSBpcyB0aGF0IHlvdSBjYW4gZXhlY3V0ZSB0aGUgZW50aXJlIHNjcmlwdCAoJ3NvdXJjZScpIG9yIHJ1biBpdCBsaW5lIGJ5IGxpbmUgYWxsIHdoaWxlIHJlY29yZGluZyB5b3VyIGNvbW1hbmRzIGluIHRoZSBzY3JpcHQgZmlsZS4NCg0KIVtdKHBpY3R1cmVzL1JndWkucG5nKQ0KDQojIyBJbnRybyB0byBSU3R1ZGlvDQoNCltSU3R1ZGlvXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9SU3R1ZGlvLyNEZXNrdG9wKSB0YWtlcyB0aGlzIEdVSSBjb25jZXB0IGEgYml0IGZ1cnRoZXIgYW5kIHByb3ZpZGVzIHlvdSB3aXRoIHNldmVyYWwgZXh0cmEgc3VwcG9ydCB3aW5kb3cuIElmIHRoZSBpZGVhIG9mIGhhdmluZyB3aW5kb3dzIGZvciB5b3VyIGVudmlyb25tZW50LCB5b3VyIGZpbGVzLCB5b3VyIHBsb3RzLCBhcyB3ZWxsIGFzIHBhY2thZ2VzIGFuZCBhIGhlbHAgdGFiIGFsbCBhdCBoYW5kIGRvZXMgbm90IGV4Y2l0ZSB5b3UsIGhvbGQgb24gdGlnaHQsIHlvdSdsbCBnZXQgdGhlcmUuDQoNCiFbXShwaWN0dXJlcy9SU3R1ZGlvLnBuZykNCg0KVGhlcmUncyBhbHNvIGEgbG90IG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgUlN0dWRpbyBvbiB0aGVpciBbY2hlYXRzaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMDEvcnN0dWRpby1JREUtY2hlYXRzaGVldC5wZGYpLiBPbiB0aGUgc3ViamVjdCBvZiBjaGVhdHNoZWV0cywgUlN0dWRpbyBoYXMgZGV2ZWxvcGVkIHNldmVyYWwgKipzdXBlciB1c2VmdWwqKiBbY2hlYXRzaGVldHNdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pOyBzZXJpb3VzbHksIHlvdSBwcm9iYWJseSB3aWxsIHdhbnQgdG8gcHJpbnQgbW9zdCBvZiB0aGVzZSBhbmQgcHV0IHRoZW0gb24gdGhlIHdhbGwgaW4geW91ciBvZmZpY2UuDQoNCiMjIEVub3VnaCB0YWxrISBMZXQncyBnZXQgY29kaW5nISBUaGUgRnVuZGFtZW50YWxzDQpZb3UgcmVhZCBteSBtaW5kISBIb3dldmVyLCBiZWZvcmUgd2UgZ2V0IHRvIGNsZWFuaW5nIHRoZSBkYXRhLCB3ZSBuZWVkIHRvIGNvdmVyIGEgZmV3IFIgZnVuZGFtZW50YWxzIHNvIHRoYXQgd2hhdCB3ZSBkbyBpbiBsYXRlciBzdGVwcyBtYWtlcyBzZW5zZS4NCg0KR28gYmFjayB0byB0aGUgcHJvamVjdCBmb2xkZXIgeW91IGRvd25sb2FkZWQgZHVyaW5nIHRoZSBbT3BlblJlZmluZSBsZXNzb25dKGh0dHBzOi8vcmVtaS1kYWlnbGUuZ2l0aHViLmlvLzIwMTctQ0hPTmUtRGF0YS9jbGVhbmluZy5odG1sI29wZW5yZWZpbmUpIGFuZCBvcGVuIHRoZSBgMjAxNy1DSE9OZS1EYXRhLlJwcm9qYCBmaWxlLiBUaGlzIGlzIGFuIFIgcHJvamVjdCBmaWxlIHRoYXQgYWxsb3dzIHlvdSB0byBzZXQgYSBudW1iZXIgb2Ygb3B0aW9ucyBmb3IgdGhlIHByb2plY3QgKHNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykpLCBidXQgZm9yIG91ciBwdXJwb3NlcyBqdXN0IGtub3cgdGhhdCB0aGUgcHJvamVjdCBmaWxlIGlzIHNldHRpbmcgdGhlICd3b3JraW5nIGRpcmVjdG9yeScsIGl0IHRlbGxzIFIgd2hlcmUgYWxsIHlvdXIgZmlsZXMgYXJlLiBXZSdsbCBnZXQgYmFjayB0byB0aGF0IGxhdGVyLg0KDQpJbiBSIHlvdSBjYW4gZG8gbWF0aCwgdHlwZSB0aGUgY29tbWFuZCBiZWxvdyBpbiB5b3VyIFIgY29uc29sZSBhbmQgaGl0IGVudGVyOg0KYGBge3J9DQoxKzENCmBgYA0KDQpZb3UgY2FuIGFsc28gYXNzaWduIHZhbHVlcyB0byB2YXJpYWJsZXMsIG9yIGluIFIgcGFybGFuY2UgYW4gJ29iamVjdCcgdXNpbmcgdGhlIGA8LWAgc3ltYm9sIChzaG9ydGtleSBgQWx0YCArIGAtYCkuDQpgYGB7cn0NCmEgPC0gMg0KYiA8LSAxKzINCmBgYA0KDQpZb3UnbGwgbm90aWNlIHRoYXQgdGhlcmUgd2FzIG5vIG91dHB1dCB0aGlzIHRpbWUgdGhlcmUgd2FzIG5vIG91dHB1dC4gVGhhdCdzIGJlY2F1c2UgdGhlIHZhbHVlIG9uIHRoZSByaWdodCBzaWRlIG9mIHRoZSBgPC1gIHN5bWJvbCBpcyBhc3NpZ25lZCB0byBhIG9iamVjdCwgaXQgZ29lcyB0byB5b3VyIGVudmlyb25tZW50ICh0b3AgcmlnaHQgd2luZG93KSBpbnN0ZWFkIG9mIGJlaW5nIG91dHB1dCB0byB0aGUgY29uc29sZS4gSW4gdGhlIDErMSBleGFtcGxlIGFib3ZlLCB0aGVyZSB3YXMgbm8gb2JqZWN0IHRvIGdvIHRvLCBzbyBpdCBkZWZhdWx0ZWQgdG8gcHJpbnRpbmcgaW4gdGhlIGNvbnNvbGUuDQoNCllvdSBjYW4gc2VlIHRoZSBjb250ZW50cyBvZiBhIG9iamVjdCBpbiB0aGUgZW52aXJvbm1lbnQgd2luZG93LCBvciBieSB0eXBpbmcgdGhlIG9iamVjdCBpbnRvIHRoZSBjb25zb2xlLiBZb3UgY2FuIGFsc28gdXNlIHRoZXNlIG9iamVjdHMgbGlrZSBhbGdlYnJhDQpgYGB7cn0NCmENCmINCmEvYg0KYGBgDQoNClVwIHRvIG5vdywgd2UndmUgYmVlbiBkZWFsaW5nIHdpdGggbnVtYmVycywgYnV0IFIgY2FuIGFsc28gZGVhbCB3aXRoIGNoYXJhY3RlciBzdHJpbmcgaWYgc3Vycm91bmRlZCBieSBzaW5nbGUgb3IgZG91YmxlIHF1b3RhdGlvbiBtYXJrcy4gQWNjb3JkaW5nIHRvIFIgaGVscCAoSSBsZWFybmVkIHRoaXMgdG9kYXkhKTogIlNpbmdsZSBhbmQgZG91YmxlIHF1b3RlcyBkZWxpbWl0IGNoYXJhY3RlciBjb25zdGFudHMuIFRoZXkgY2FuIGJlIHVzZWQgaW50ZXJjaGFuZ2VhYmx5IGJ1dCBkb3VibGUgcXVvdGVzIGFyZSBwcmVmZXJyZWQgKGFuZCBjaGFyYWN0ZXIgY29uc3RhbnRzIGFyZSBwcmludGVkIHVzaW5nIGRvdWJsZSBxdW90ZXMpLCBzbyBzaW5nbGUgcXVvdGVzIGFyZSBub3JtYWxseSBvbmx5IHVzZWQgdG8gZGVsaW1pdCBjaGFyYWN0ZXIgY29uc3RhbnRzIGNvbnRhaW5pbmcgZG91YmxlIHF1b3Rlcy4iDQoNCmBgYHtyfQ0KZiA8LSAiVGhpcyBpcyBhIGNoYXJhY3RlciBzdHJpbmcsIHlvdSBjYW4gdGVsbCBiZWNhdXNlIG9mIHRoZSBxdW90YXRpb24gbWFya3MiDQpgYGANCg0KQSBvYmplY3QgY2FuIGFsc28gY29udGFpbiBtdWx0aXBsZSB2YWx1ZXMsIHRoaXMgaXMgY2FsbGVkIGEgdmVjdG9yLiBUaGUgYDpgIHN5bWJvbCBlc3NlbnRpYWxseSBtZWFucyAndG8nDQoNCmBgYHtyfQ0KeCA8LSAxOjMNCmBgYA0KDQpBbm90aGVyIHdheSB0byBkbyB0aGF0LCB3aXRoIG1vcmUgZmxleGliaWxpdHkgaXMgdXNpbmcgdGhlIGBjKClgOyB0aGUgYyBpcyBzaG9ydCBmb3IgY29uY2F0ZW5hdGUgYW5kIHRoZSByb3VuZCBicmFja2V0cyBpbmRpY2F0ZSB0aGF0IGl0J3MgYSBmdW5jdGlvbi4gU28gdGhpcyBjb25jYXRlbmF0ZSBmdW5jdGlvbiB3aWxsIGNvbmNhdGVuYXRlIGFsbCB0aGUgJ2FyZ3VtZW50cycgKHRoaW5ncyBpbnNpZGUgdGhlIHJvdW5kIGJyYWNrZXRzKSB3aGljaCBhcmUgc2VwYXJhdGVkIGJ5IGNvbW1hcy4gWW91IGNhbiBhbHNvIGNvbWJpbmUgdGhlc2Ugc3RyYXRlZ2llcw0KDQpgYGB7cn0NCnggPC0gYygxLDMsNSkNCnkgPC0gYygxOjQsNiw4KQ0KYGBgDQoNClRoZXJlIGFyZSBtYW55IGZ1bmN0aW9ucywgYnV0IHRoZXkgYWxsIGZvbGxvdyB0aGUgZm9ybWF0IGBmdW5jdGlvbk5hbWUoYXJndW1lbnQxLGFyZ3VtZW50Mixhcmd1bWVudDMsLi4uKWAgd2hlcmUgdGhlICdhcmd1bWVudHMnIGFyZSB0aGUgaW5wdXQgdG8gdGhlIGZ1bmN0aW9uLiBTb21lIGFyZSBmYWlybHkgc3RyYWlnaHRmb3J3YXJkOg0KDQpgYGB7cn0NCm1lYW4oeSkNCmBgYA0KQnV0IGV2ZW4gdGhlbiB0aGVyZSBhcmUgc29tZSBzdXJwcmlzZXMsIGxldCdzIGxvb2sgYXQgdGhlIGhlbHAgZmlsZSBmb3IgYG1lYW4oKWAuIFRvIGRvIHRoYXQgeW91IGNhbjoNCi0gaWYgeW91IGFyZSBvbiB0aGUgYWN0aXZlIGxpbmUgaW4gdGhlIGNvbnNvbGUgb3IgYW55d2hlcmUgaW4gYSBzY3JpcHQsIHB1dCB5b3VyIGN1cnNvciBvbiB0aGUgZnVuY3Rpb24gYW5kIHByZXNzIGBGMWANCi0gaW4gdGhlIGNvbnNvbGUsIHR5cGUgYD9tZWFuYCAob3IgYD8/bWVhbmAgaWYgeW91ciBub3Qgc28gc3VyZSBgbWVhbmAgaXMgdGhlIG5hbWUgb2YgdGhlIGZ1bmN0aW9uKQ0KLSBmaW5kIHRoZSBoZWxwIHdpbmRvdyAob25lIG9mIHRoZSB0YWJzIGZvciB0aGUgYm90dG9tIHJpZ2h0IHdpbmRvdykgYW5kIHVzZSB0aGUgc2VhcmNoIGJhcg0KLSBhbHNvLCB3aGVuIGFsbCBlbHNlIGZhaWxzLCBHb29nbGUgaXMgeW91ciBmcmllbmQhDQoNCkFueSBtZXRob2Qgc2hvdWxkIGdldCB5b3UgdG8gc29tZXRoaW5nIGxpa2UgdGhpczoNCiFbXShwaWN0dXJlcy9oZWxwLnBuZykNCkluIFIgaW4gbW9zdCBjYXNlcyB5b3UgY291bGQgdXNlIGA9YCBpbnN0ZWFkIG9mIHRoZSBgPC1gIHN5bWJvbCB3aXRoIG5vIHByb2JsZW1zIHdoZW4geW91IGFyZSBhc3NpZ25pbmcgYSB2YWx1ZSB0byBhIG9iamVjdC4gSG93ZXZlciwgaXQgaXMgYmVzdCBwcmFjdGljZSB0byB1c2UgYDwtYCB3aGVuIGFzc2lnbmluZyBlbnZpcm9ubWVudCBvYmplY3RzIGFuZCBgPWAgd2hlbiBkZWZpbmluZyBmdW5jdGlvbiBhcmd1bWVudHMuIE9oLCBhbmQgYE5BYCBpbiBSIG1lYW5zIOKAmE5vdCBBdmFpbGFibGXigJkgLyBNaXNzaW5nIFZhbHVlcy4gTGlrZSBzbzoNCmBgYHtyfQ0KeCA8LSBjKDEsMiw1LDcsODgsMyw0LDIsNCw2LDcsTkEpDQoNCm1lYW4oeCkNCg0KbWVhbih4LCBuYS5ybSA9IFRSVUUpDQpgYGANCg0KSSBhbHNvIHNudWNrIGEgYFRSVUVgIGluIHRoZXJlOyBgVFJVRWAgYW5kIGBGQUxTRWAgYXJlIGNhbGxlZCBsb2dpY2FsIGFuZCBhcmUgZGlzdGluY3QgZnJvbSBudW1lcmljIG9yIGNoYXJhY3RlciBzdHJpbmdzLiBUaGV5IGFyZSBzb21ldGltZXMgdXNlZCBhcyBhcmd1bWVudHMgdmFsdWVzLCBidXQgdGhleSBjYW4gYWxzbyB1c2VkIHRvIHRlc3QgdGhpbmdzLiBUaGUgYD09YCBhc2tzIGlmIGJvdGggc2lkZXMgYXJlIGVxdWFsIChzaW5jZSB0aGUgc2luZ2xlIGA9YCBpcyBhbHJlYWR5IHVzZWQgZm9yIG90aGVyIHRoaW5ncyksIGFuZCB0aGUgYCE9YCBhc2tzIGlmIGJvdGggc2lkZXMgYXJlIG5vdCBlcXVhbC4NCg0KYGBge3J9DQoyPT0xDQoyPT0yDQoyIT0xDQpgYGANCiMjIENyZWF0aW5nIHlvdXIgb3duIHNjcmlwdHMNClVwIHVudGlsIG5vdywgd2UndmUgYmVlbiBwbGF5aW5nIGluIHRoZSBjb25zb2xlIHdoaWNoIG1lYW5zIHRoZSAnaW5zdHJ1Y3Rpb25zJyB3ZSBuZWVkIHRvIHNhdmUgdG8gcmVwcm9kdWNlIG91ciBzY2llbmNlIGFyZSBsb3N0ICh3ZWxsIG5vdCByZWFsbHksIHRoZXkgY2FuIGJlIHJldHJpZXZlZCBmcm9tIHRoZSBIaXN0b3J5IHRhYiBpbiB0aGUgdG9wIHJpZ2h0LCBvciB0aGUgY29uc29sZSBpZiBpdCBoYXNuJ3Qgcm9sbGVkIG9mZiB0aGUgc2NyZWVuKS4gSXQgaXMgYSBnb29kIGlkZWEgdG8gZGV2ZWxvcCB5b3VyIGFuYWx5c2lzIHVzaW5nIGEgc2NyaXB0IGZpbGUgKHRob3NlIHNpbXBsZSB0ZXh0IGZpbGVzIHdpdGggdGhlIGAuUmAgZXh0ZW5zaW9uIEkgd2FzIHRhbGtpbmcgYWJvdXQgZWFybGllcikgYmVjYXVzZSB5b3UgY2FuIHNhdmUgeW91ciBjb2RlIGVhc2lseS4NCg0KVG8gY3JlYXRlIGEgbmV3IHNjcmlwdCwgeW91ciBjYW4gY2xpY2sgb24gdGhlIGxpdHRsZSBwYXBlciB3aXRoIHRoZSBwbHVzIHN5bWJvbCAoc2VlIGJlbG93KSwgb3IgeW91IGNhbiBoaXQgYEN0cmxgK2BTaGlmdGArYE5gIChXaW5kb3dzKSwgb3IgYENvbW1hbmRgK2BTaGlmdGArYE5gIChNYWMpLCBhbmQgaWYgdGhhdCdzIG5vdCBlbm91Z2ggb3B0aW9ucywgeW91IGNhbiBjbGljayBgRmlsZSA+IE5ldyBGaWxlID4gTmV3IFNjcmlwdGANCg0KIVtdKHBpY3R1cmVzL25ld3NjcmlwdC5wbmcpDQoNClRoZXNlIHNjcmlwdHMgYXJlIGRlc2lnbmVkIHRvIHJlYWQgYnkgUiBmcm9tIHRvcCB0byBib3R0b20gd2hlbiB5b3UgaGl0IHRoZSAhW10ocGljdHVyZXMvc291cmNlLnBuZykgYnV0dG9uLCBvciBgQ3RybGArYFNoaWZ0YCtgU2AgKFdpbmRvd3MpLCBvciBgQ29tbWFuZGArYFNoaWZ0YCtgU2AgKE1hYykuIEFsdGVybmF0aXZlbHksIHlvdSBjYW4gcnVuIHBvcnRpb25zIG9mIHlvdXIgY29kZSB3aXRoIGBDdHJsYCtgRW50ZXJgIChXaW5kb3dzKSwgb3IgYENvbW1hbmRgK2BFbnRlcmAgKE1hYykgYW5kIGVpdGhlciBwdXR0aW5nIHlvdXIgY3Vyc29yIG9uIGEgbGluZSB0byBydW4gdGhlIGVudGlyZSBsaW5lLCBvciBoaWdobGlnaHRpbmcgYSBzdWJzZWN0aW9uIG9mIGNvZGUgdG8gcnVuIGp1c3QgdGhhdCBwb3J0aW9uLiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gYnVpbGQgbXVsdGktc3RlcCBkYXRhIHByb2Nlc3NpbmcgYW5kIGFuYWx5c2lzIHNjcmlwdHMuDQoNCj4qKlByby10aXAqKg0KPklmIHlvdSBkb24ndCB3YW50IFIgdG8gcmVhZCBzb21ldGhpbmcsIHVzIHRoZSBgI2AuIEFueXRoaW5nIHRoYXQgaXMgcHJlY2VkZWQgYnkgYSBgI2AgaXMgcmVnYXJkZWQgYXMgYSAnY29tbWVudCcgYnkgUiBhbmQgaXQgZG9lcyBub3QgdHJ5IHRvIGV4ZWN1dGUgdGhvc2UgbGluZXMgKGkuZS4gUiBpZ25vcmVzIGFueXRoaW5nIGFmdGVyIGEgYCNgKS4NCj4gVGhpcyBpcyBhbHNvIHVzZWZ1bCBpZiB5b3Ugd2FudCB0byBhdm9pZCBydW5uaW5nIGEgZmV3IGxpbmVzIG9mIGNvZGUgd2hlbiB5b3UgYXJlIGRldmVsb3BpbmcgeW91ciBzY3JpcHQuIEluc3RlYWQgb2YgdHlwaW5nIGEgYCNgIGluIGZyb250IG9mIGVhY2ggbGluZSBvZiBjb2RlLCB5b3UgY2FuIGhpZ2hsaWdodCB0aGUgbGluZXMgeW91IHdhbnQgY29tbWVudGVkIG91dCBhbmQgaGl0IGBDdHJsYCtgU2hpZnRgK2BDYCAoV2luZG93cyksIG9yIGBDb21tYW5kYCtgU2hpZnRgK2BDYCAoTWFjKS4gTWFnaWMhDQoNCkNvbW1lbnRpbmcgaXMgc3VwZXIgdXNlZnVsIHRvIGluY2x1ZGUgaHVtYW4gcmVhZGFibGUgaW5zdHJ1Y3Rpb25zL2RvY3VtZW50YXRpb24gaW4geW91ciBjb2RlLiBMZXQncyBnaXZlIHRoaXMgYSB0cnksIHdyaXRlIHRoaXMgY2h1bmsgb2YgY29kZSBpbnRvIHlvdXIgc2NyaXB0LCB0aGVuIHJ1biBpdCBsaW5lIGJ5IGxpbmUuDQpgYGB7cn0NCnggPC0gMQ0KDQp4IDwtIDINCg0KIyB4IDwtIDMNCmBgYA0KV2hhdCBpcyB0aGUgdmFsdWUgb2YgYHhgIGFmdGVyIHJ1bm5pbmcgYWxsIHRoZSBsaW5lcyBhbmQgd2h5Pw0KDQojIyBJbmRleGluZyBhbmQgZGltZW5zaW9ucw0KDQpXZSBhbHJlYWR5IG1lbnRpb25lZCB0aGF0IHdlIGNhbiBoYXZlIG11bHRpcGxlIHZhbHVlcyBpbiBhbiBvYmplY3QsIGJ1dCBzbyBmYXIgdGhpcyBoYXMgYmVlbiBpbiBvbmx5IDEgZGltZW5zaW9uLCBidXQgdXNpbmcgYG1hdHJpeCgpYCAoMkQpIGFuZCBgYXJyYXkoKWAgKD4yRCkgd2UgY2FuIHN0b3JlIG51bWJlcnMgaW4gbXVsdGlwbGUgZGltZW5zaW9ucw0KDQpgYGB7cn0NCiMgbWFrZSBhIG1hdHJpeA0KeCA8LSBtYXRyaXgoZGF0YSA9IGMoMSwxLDIsNSwzLDQpLCBucm93ID0gMiwgbmNvbCA9IDMpDQoNCngNCg0KIyBtYWtlIGFuIGFycmF5DQoNCnkgPC0gYXJyYXkoZGF0YSA9IGMoMTo4KSwgZGltID0gYygyLDIsMikpDQoNCnkNCmBgYA0KDQo+KipFeGVyY2lzZSoqDQo+DQo+IFRyeSBzdG9yaW5nIG51bWVyaWMgYW5kIGNoYXJhY3RlciBzdHJpbmdzIGluIGEgbWF0cml4IChvciBhbiBhcnJheSkuIFdoYXQgaGFwcGVucyB0byB0aGUgbnVtZXJpY3M/DQoNClRoYXQncyBncmVhdCB0aGF0IHdlIGNhbiBzdG9yZSB0aGlzIGRhdGEgaW4gbXVsdGlwbGUgZGltZW5zaW9ucywgYnV0IGhvdyBkbyBJIGdldCBpdCBiYWNrPyBUaGF0J3Mgd2hhdCBgW11gIGFyZSBmb3IhDQoNCmBgYHtyfQ0KIyBpbiAxRA0KeCA8LSBjKDEsMiwzLDQsNiwzNCwyLDEsNSw2LDcpDQojIGlmIHdlIHdhbnQgdGhlIDd0aCB2YWx1ZQ0KeFs3XQ0KIyBpZiB3ZSB3YW50IHRoZSAybmQgYW5kIDd0aCB2YWx1ZQ0KeFtjKDIsNyldDQojIGlmIHdlIHdhbnQgb25seSB2YWx1ZXMgZ3JlYXRlciB0aGFuIDEwDQp4W3g+MTBdDQojIHdob2EsIHRoYXQgYmxldyBteSBtaW5kISBIb3cgZGlkIHRoYXQgd29yaz8NCiMgd2VsbCBpbmRleGluZyB3b3JrcyBieSBnaXZpbmcgdGhlIG51bWVyaWMgaW5kZXgsIG9yIGEgbG9naWNhbCB2ZWN0b3IgdGhlIGxlbmd0aCBvZiB0aGUgdmVjdG9yIHdlJ3JlIHdvcmtpbmcgd2l0aA0KIyBzbyB4PjEwIHByb2R1Y2VzIGEgbG9naWNhbCB2ZWN0b3IgdGhlIGxlbmd0aCBvZiB4DQp4PjEwDQoNCiMgaW4gMkQNCnggPC0gbWF0cml4KGRhdGEgPSBjKDEsMSwyLDUsMyw0KSwgbnJvdyA9IDIsIG5jb2wgPSAzKQ0KIyB0aGlzIHdvcmtzIHNpbWlsYXIsIGJ1dCB5b3UgbmVlZCB0byBwcm92aWRlIDIgbnVtYmVycyAob3IgMiB2ZWN0b3JzIG9mIG51bWJlcnMpLCBmb3Igcm93IG51bWJlciBhbmQgZm9yIGNvbHVtbiBudW1iZXINCnhbMSwzXQ0KeFtjKDEsMiksMV0NCiMgaWYgeW91IGxlYXZlIG9uZSBkaW1lbnNpb24gYmxhbmssIHRoZSB3aG9sZSByb3cgb3IgY29sdW1uIHdpbGwgYmUgcmV0dXJuZWQNCnhbLDFdDQp4WzIsXQ0KDQojIGluIDNELCBhZGQgYW5vdGhlciBkaW1lbnNpb24hDQp4IDwtIGFycmF5KGRhdGEgPSBjKDE6MjQpLCBkaW0gPSBjKDIsMyw0KSkNCnhbMSwyLDNdDQp4WywsM10NCmBgYA0KDQoNCkRhdGEgZnJhbWVzIChgZGF0YS5mcmFtZSgpYCkgYXJlIGEgc3BlY2lhbCB0eXBlIG9mIG1hdHJpeCB0aGF0IGNhbiBob2xkIG51bWVyaWMgYW5kIGNoYXJhY3RlciBzdHJpbmdzIChhbmQgbG9naWNhbHMsIGZhY3RvcnMsIGdlb21ldHJpZXMsIGV0Yykgd2l0aG91dCBoYXZpbmcgdG8gY29udmVydCB0aGVtIGFsbCB0byBhIHNpbmdsZSB0eXBlLiBJdCdzIGVmZmVjdGl2ZWx5IGEgY29sbGVjdGlvbiBvZiB2ZWN0b3JzIG9mIHRoZSBzYW1lIGxlbmd0aC4NCg0KYGBge3J9DQojIG1ha2UgYSBkYXRhIGZyYW1lDQp4IDwtIGRhdGEuZnJhbWUobnVtcyA9IGMoMSwyLDMpLA0KICAgICAgICAgICAgICAgIGNoYXJzID0gYygib25lIiwidHdvIiwidGhyZWUiKSwNCiAgICAgICAgICAgICAgICBsb2dpcyA9IGMoVFJVRSwgRkFMU0UsIFRSVUUpKQ0KDQojIHByaW50IHRoZSB3aG9sZSB0aGluZyB0byB0aGUgY29uc29sZQ0KeA0KDQojIG9yIGZvciBhIHZlcnkgYmlnIGRhdGFmcmFtZSwgeW91IG1heSB3YW50IHRvIHVzZSBoZWFkIHRvIHNlZSB0aGUgdG9wIDUgcm93cw0KaGVhZCh4KQ0KDQojIHVzZSB0aGUgc3RyIGZ1bmN0aW9uIHRvIHNlZSBpdCdzIHN0cnVjdHVyZQ0Kc3RyKHgpDQoNCmBgYA0KPioqUHJvLXRpcCoqDQo+DQo+IERpZCB5b3Ugbm90aWNlIHRoYXQgdGhlIHN0cnVjdHVyZSBvZiBjaGFycyB3YXMgYEZhY3RvcmAgYW5kIG5vdCBgY2hyYD8gRmFjdG9ycyBhcmUgYSBzcGVjaWFsIHR5cGUgb2Ygb2YgY2hhcmFjdGVyIHN0cmluZyB2ZWN0b3IgdGhhdCBjb25zZXJ2ZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHZlY3RvcidzICdsZXZlbHMnIChhbmQgeW91IGNhbiBhbHNvIG9yZGVyIHRob3NlIGxldmVscykuIE1hbnkgZnVuY3Rpb25zLCBzdWNoIGFzIGBkYXRhLmZyYW1lKClgIGhhdmUgYW4gYXJndW1lbnQgY2FsbGVkIGBzdHJpbmdzQXNGYWN0b3JzYCBhbmQgaXMgdXN1YWxseSBzZXQgdG8gYFRSVUVgIGJ5IGRlZmF1bHQsIHlvdSBtYXkgd2FudCB0byBzZXQgdGhpcyB0byBgRkFMU0VgLiBPZnRlbiwgdGhlc2UgYXJlIGludGVyY2hhbmdlYWJsZSwgYnV0IEkgaGF2ZSBoYWQgKiptYW55IGZydXN0cmF0aW5nIGVycm9ycyoqIGJlY2F1c2UgSSBtaXN0YWtlbmx5IGhhZCBhIGNoYXJhY3RlciBzdHJpbmcgd2hlcmUgSSBuZWVkZWQgYSBmYWN0b3IgYW5kIHZpY2UgdmVyc2EuIEJlIGF3YXJlIHRoYXQgdGhleSBhcmUgZGlmZmVyZW50IGFuZCBub3Qga25vd2luZyB3aGljaCBvbmUgeW91IGhhdmUgY2FuIGxlYWQgdG8gZXJyb3JzLiBZb3UgY2FuIGVhc2lseSBjb252ZXJ0IGJhY2sgYW5kIGZvcnRoIHdpdGggYGFzLmZhY3RvcmAgKG9yIGBhcy5vcmRlcmVkKClgKSBhbmQgYGFzLmNoYXJhY3RlcigpYA0KDQpgYGB7cn0NCiMgbGV0J3MgdHJ5IGhhdmluZyBib3RoIHN0cmluZ3MgYW5kIGZhY3RvcnMgaW4gdGhlIGRhdGEgZnJhbWUNCiMgbWFrZSBhIGRhdGEgZnJhbWUNCnggPC0gZGF0YS5mcmFtZShudW1zID0gYygxLDIsMyksDQogICAgICAgICAgICAgICAgY2hhcnMgPSBjKCJvbmUiLCJ0d28iLCJ0aHJlZSIpLA0KICAgICAgICAgICAgICAgIGZhY3RzID0gYXMuZmFjdG9yKGMoIm9uZSIsInR3byIsInRocmVlIikpLA0KICAgICAgICAgICAgICAgIGxvZ2lzID0gYyhUUlVFLCBGQUxTRSwgVFJVRSksDQogICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIHByaW50IHRoZSB3aG9sZSB0aGluZyB0byB0aGUgY29uc29sZQ0KeA0KDQojIG9yIGZvciBhIHZlcnkgYmlnIGRhdGFmcmFtZSwgeW91IG1heSB3YW50IHRvIHVzZSBoZWFkIHRvIHNlZSB0aGUgdG9wIDUgcm93cw0KaGVhZCh4KQ0KDQojIHVzZSB0aGUgc3RyIGZ1bmN0aW9uIHRvIHNlZSBpdCdzIHN0cnVjdHVyZQ0Kc3RyKHgpDQoNCmBgYA0KDQpJbmRleGluZyBpbiBkYXRhZnJhbWVzIGNhbiBiZSB0aGUgc2FtZSBhcyBmb3IgYSAyRCBtYXRyaXgsIG9yIHlvdSBjYW4gdXNlIGAkYCB0byBhY2Nlc3MgY29sdW1ucyBieSBuYW1lDQoNCmBgYHtyfQ0KIyBtYWtlIGEgZGF0YSBmcmFtZQ0KeCA8LSBkYXRhLmZyYW1lKG51bXMgPSBjKDEsMiwzKSwNCiAgICAgICAgICAgICAgICBjaGFycyA9IGMoIm9uZSIsInR3byIsInRocmVlIiksDQogICAgICAgICAgICAgICAgZmFjdHMgPSBhcy5mYWN0b3IoYygib25lIiwidHdvIiwidGhyZWUiKSksDQogICAgICAgICAgICAgICAgbG9naXMgPSBjKFRSVUUsIEZBTFNFLCBUUlVFKSwNCiAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCnhbMSwyXQ0KeFssMl0NCngkY2hhcnMNCg0KIyBhbmQgeW91IGNhbiBhbHNvIHRyZWF0IHRoZXNlIGNvbHVtbnMgYXMgMUQgdmVjdG9ycw0KeCRjaGFyc1sxXQ0KDQpgYGANCg0KIyMgUmVhZGluZyBpbiBkYXRhDQpUaGF0J3MgYWxsIGdyZWF0LCBidXQgb3VyIGRhdGEgaXMgc3RvcmUgaW4gYSBgLmNzdmAgZmlsZSwgaG93IGRvIHdlIGdldCB0aGF0PyBUaGUgc2ltcGxlc3Qgd2F5IGlzIHRvIHVzZSB0aGUgYHJlYWQuY3N2KClgIGZ1bmN0aW9uLiBCdXQgd2UgbmVlZCB0byBrbm93IHdoZXJlIG9uIHRoZSBjb21wdXRlciB0aGUgZmlsZSBpcyBzdG9yZWQuDQoNClRoZSBmaXJzdCBzdGVwIGlzIHRvIGxvYWQgeW91ciBkYXRhIGFuZCB0byBjcmVhdGUgYSBwcm9qZWN0LiBJZiB5b3UgaGF2ZW4ndCBkb25lIHNvIGFscmVhZHksIHBsZWFzZSBkb3dubG9hZCB0aGUgW3ppcCBmaWxlXShodHRwczovL2dpdGh1Yi5jb20vcmVtaS1kYWlnbGUvMjAxNy1DSE9OZS1EYXRhL2FyY2hpdmUvZ2gtcGFnZXMuemlwKSB0aGUgZW50aXJlICBnaXRodWIgW3Byb2plY3QgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL3JlbWktZGFpZ2xlLzIwMTctQ0hPTmUtRGF0YS8pIChjb250YWlucyBkYXRhIGZpbGVzLCBhbmQgYWxsIHRoZSBSIHNjcmlwdHMgdXNlZCB0byBtYWtlIHRoaXMgdmVyeSB3ZWJzaXRlISkgYW5kIGV4dHJhY3QgaXQgc29tZXdoZXJlIGNvbnZlbmllbnQuDQoNCkdvIGJhY2sgdG8gdGhlIHByb2plY3QgZm9sZGVyIHlvdSBleHRyYWN0ZWQgYW5kIG9wZW4gdGhlIGAyMDE3LUNIT05lLURhdGEuUnByb2pgIGZpbGUuIFRoaXMgaXMgYW4gUiBwcm9qZWN0IGZpbGUgdGhhdCBhbGxvd3MgeW91IHRvIHNldCBhIG51bWJlciBvZiBvcHRpb25zIGZvciB0aGUgcHJvamVjdCAoc2VlIFtoZXJlXShodHRwczovL3N1cHBvcnQucnN0dWRpby5jb20vaGMvZW4tdXMvYXJ0aWNsZXMvMjAwNTI2MjA3LVVzaW5nLVByb2plY3RzKSksIGJ1dCBmb3Igb3VyIHB1cnBvc2VzIGp1c3Qga25vdyB0aGF0IHRoZSBwcm9qZWN0IGZpbGUgaXMgc2V0dGluZyB0aGUgJ3dvcmtpbmcgZGlyZWN0b3J5JywgaXQgdGVsbHMgUiB3aGVyZSBhbGwgeW91ciBmaWxlcyBhcmUuDQoNCmBgYHtyfQ0KIyB5b3UgY2FuIGhhcmQgY29kZSB0aGUgd2hvbGUgZmlsZSBwYXRoLCBidXQgdHJ5IHRvIG5ldmVyIGRvIHRoYXQhDQojIHlvdXIgY29tcHV0ZXIgbWF5IG5vdCBoYXZlIGEgQyBkcml2ZSwgYW5kIHlvdXIgbmFtZSBhbG1vc3QgY2VydGFpbmx5IGlzIG5vdCBSZW1pLCBzbyB0aGlzIHdpbGwgbm90IHdvcmsgZm9yIHlvdQ0KDQojIGxhcnZhbEFidW5kYW5jZSA8LSByZWFkLmNzdigiQzovVXNlcnMvUmVtaS1Xb3JrL0Rlc2t0b3AvMjAxNy1DSE9OZS1EYXRhL3Jhd2RhdGEvbGFydmFsIGFidW5kYW5jZS5jc3YiKQ0KDQojIFRoZSBhYm92ZSBpcyB1bnN1cnByaXNpbmdseSBub3QgcmVwcm9kdWNpYmxlISBBbHdheXMgdXNlIHJlbGF0aXZlIHBhdGhzIQ0KIyBSZWxhdGl2ZSBwYXRocyBhcmUgYSBzaG9ydGVuZWQgdmVyc2lvbiBvZiB0aGUgYWJvdmUsIHlvdSBvbmx5IG5lZWQgdG8gdHlwZSB3aGF0IGNvbWVzIGFmdGVyIHRoZSBwcm9qZWN0IGRpcmVjdG9yeQ0KIyBSRU1JTkRFUjogVGhlIHByb2plY3QgZGlyZWN0b3J5IGlzIHdoZXJlIHRoZSAuUnByb2ogZmlsZSBpcyBzdG9yZWQNCg0KbGFydmFsQWJ1bmRhbmNlIDwtIHJlYWQuY3N2KCJyYXdkYXRhL2xhcnZhbCBhYnVuZGFuY2UuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIHdyaXRpbmcgaXQgdGhpcyB3YXkgKHJlbGF0aXZlIHBhdGgpIG1lYW5zIHRoYXQgeW91ciBwcm9qZWN0IGlzIHJlcHJvZHVjaWJsZSBzaW5jZSB5b3UgY2FuIG1vdmUgdGhpcyB3aG9sZSBkaXJlY3RvcnkgdG8gYW5vdGhlciBsb2NhdGlvbiBvbiB5b3VyIGNvbXB1dGVyIE9SIEFOWSBPVEhFUiBDT01QVVRFUiEhIQ0KDQpgYGANCg0KVGhpcyBkYXRhIGNvbWVzIGZyb20gb25lIG9mIFJlbWkncyBQaEQgdGhlc2lzIGNoYXB0ZXJzW14xXSB0aGF0IHdhcyBwYXJ0IG9mIHRoZSBmaXJzdCBDSE9OZS4gSSBtb2RpZmllZCBpdCBmcm9tIHRoZSBvcmlnaW5hbCB2ZXJzaW9uIGFyY2hpdmVkIG9uIFtEcnlhZF0oaHR0cDovL2RhdGFkcnlhZC5vcmcvaGFuZGxlLzEwMjU1L2RyeWFkLjU5NDgyKSBzbyB3ZSB3b3VsZCBoYXZlIGNsZWFuaW5nIHRvIGRvIQ0KDQpbXjFdOiBEYWlnbGUgUk0sIE1ldGF4YXMgQSwgZGVZb3VuZyBCICgyMDE0KSBCYXktc2NhbGUgcGF0dGVybnMgaW4gdGhlIGRpc3RyaWJ1dGlvbiwgYWdncmVnYXRpb24gYW5kIHNwYXRpYWwgdmFyaWFiaWxpdHkgb2YgbGFydmFlIG9mIGJlbnRoaWMgaW52ZXJ0ZWJyYXRlcy4gTWFyaW5lIEVjb2xvZ3kgUHJvZ3Jlc3MgU2VyaWVzIDUwMzoxMzktMTU2LiBodHRwOi8vZHguZG9pLm9yZy8xMC4zMzU0L21lcHMxMDczNA0KDQojIyBDbGVhbmluZyBkYXRhIGluIFINCg0KQ29tbW9uIGVycm9ycyBpbiBkYXRhIGFyZToNCg0KLSB0cmFpbGluZyBvciBsZWFkaW5nIHdoaXRlIHNwYWNlcyBpbiBjaGFyYWN0ZXIgc3RyaW5ncw0KLSBkYXRhIGVudGVyZWQgaW4gZGlmZmVyZW50IGZvcm1hdHMgKGUuZy4gbnVtYmVycyBhbmQgY2hhcmFjdGVyIHN0cmluZ3MgaW4gb25lIGNvbHVtbikNCi0gZGF0YSBlbnRlcmVkIHdpdGggd3JvbmcgdW5pdHMNCg0KDQpUaGUgZmlyc3Qgc3RlcCBJIGFsd2F5cyB0YWtlIGlzIHRvIG1ha2Ugc3VyZSB0aGF0IHRoZSBkYXRhIGxvYWRlZCBpbiBhcyBleHBlY3RlZC4gSGVhZCBvdmVyIHRvIHRoZSBFbnZpcm9ubWVudCB3aW5kb3cgYW5kIGNsaWNrIG9uIGBkYXRhYC4gWW91IGNhbiBkbyAodGVtcG9yYXJ5KSBzb3J0aW5nIGFuZCBmaWx0ZXJpbmcgb2YgdGhlIGRhdGEgaW4gdGhlIGRhdGEgdmlld2VyLiBJIGFsc28gdXNlIHRoZSBgc3RyKClgIGZ1bmN0aW9uIHRvIG1ha2Ugc3VyZSBhbGwgdGhlIHZhcmlhYmxlcyAoY29sdW1ucykgYXJlIG9mIHRoZSBjb3JyZWN0IHR5cGUuDQpgYGB7cn0NCnN0cihsYXJ2YWxBYnVuZGFuY2UpDQpgYGANCg0KSWYgSSB3YW50ZWQgdG8gbWFrZSBhIGNvcnJlY3Rpb24gbWFudWFsbHksIHdlIGNhbiB1c2UgaW5kZXhpbmc6DQpgYGB7cn0NCiMgc2F5IGZvciBleGFtcGxlLCBJIGtuZXcgdGhhdCB0aGUgc2Vjb25kIG9ic2VydmF0aW9uIHdhcyBpbiBmYWN0IHRha2VuIGF0IDEyIG0gZGVwdGgNCmxhcnZhbEFidW5kYW5jZSRkZXB0aFsyXQ0KDQojIHRoZSB2YWx1ZSBpbiB0aGUgZGF0YSBmcmFtZSBpcyBpbmRlZWQgMyBXUk9ORyEgTGV0LHMgY29ycmVjdCBpdA0KbGFydmFsQWJ1bmRhbmNlJGRlcHRoWzJdIDwtIDEyDQpoZWFkKGxhcnZhbEFidW5kYW5jZSkNCg0KIyBUaGUgb3RoZXIgaXNzdWUgeW91IG1heSBub3RpY2UgaXMgdGhhdCBteSBtb250aHMgdmFsdWVzIGFyZSBtb3N0bHkgbnVtYmVycywgYnV0IHRoZXJlJ3MgYXQgbGVhc3QgMSAiQXVndXN0IiBpbiB0aGVyZQ0KIyBXZSBjb3VsZCBjb3JyZWN0IGp1c3QgdGhhdCBvbmUgd2Ugc2VlIG9uIGxpbmUgNA0KbGFydmFsQWJ1bmRhbmNlJG1vbnRoWzRdIDwtIDgNCg0KIyBPciB3ZSBjb3VsZCBnZXQgcmlkIG9mIGFsbCB0aGUgIkF1Z3VzdCIncyBpbiBvbmUgcGFzcw0KbGFydmFsQWJ1bmRhbmNlJG1vbnRoW2xhcnZhbEFidW5kYW5jZSRtb250aD09IkF1Z3VzdCJdIDwtIDgNCg0KIyBidXQgdGhhdCBjb2x1bW4gaXMgc3RpbGwgYSBjaGFyYWN0ZXIgc3RyaW5nLCBsZXQncyBjb252ZXJ0IGl0IHRvIG51bWVyaWMNCmxhcnZhbEFidW5kYW5jZSRtb250aCA8LSBhcy5udW1lcmljKGxhcnZhbEFidW5kYW5jZSRtb250aCkNCg0Kc3RyKGxhcnZhbEFidW5kYW5jZSkNCg0KYGBgDQoNCg0KSWYgeW91IHdlcmUgd29uZGVyaW5nIGhvdyBtYW55IHllYXJzIEkgaGFkIHNhbXBsZWQsIHlvdSBjb3VsZCBkbzoNCmBgYHtyfQ0KdW5pcXVlKGxhcnZhbEFidW5kYW5jZSR5ZWFyKQ0KDQojIG9yIGZvciB0aW1lDQp1bmlxdWUobGFydmFsQWJ1bmRhbmNlJHRpbWUpDQoNCiMgYXMgeW91IGNhbiBzZWUgdGhlcmUgYXJlIGEgZmV3IHRyYWlsaW5nIHdoaXRlIHNwYWNlcywgdG8gZG8gdGV4dCBzdWJzdGl0dXRpb25zLCBsZXQncyB1c2UgZ3N1YigpDQojIGJ1dCBmaXJzdCBsZXQncyBzZWUgaG93IHRoaXMgd29ya3M7IGl0IG1hdGNoZXMgYSBwYXR0ZXJuIGluIHggYW5kIHJlcGxhY2VzIGl0Lg0KDQpnc3ViKHBhdHRlcm4gPSAiQUJDIixyZXBsYWNlbWVudCA9ICJYWVoiLHggPSAiVFVWV0FCQyIpDQoNCmdzdWIocGF0dGVybiA9ICJkb2Vzbid0IHdvcmsiLHJlcGxhY2VtZW50ID0gIndvcmtzIix4ID0gIklmIHRoaXMgc2VudGVuY2Ugbm8gbG9uZ2VyIGNvbnRhaW5zIHRoZSBwYXR0ZXJuLCB0aGVuIGdzdWIgZG9lc24ndCB3b3JrIikNCg0KIyBTbyBub3csIGxldCdzIHVzZSBnc3ViIHRvIHJlbW92ZSB0aG9zZSBzcGFjZXMuIFRoZSBwYXR0ZXIgd2UgYXJlIG1hdGNoaW5nIGlzIGp1c3QgYSBzcGFjZSBhbmQgdGhlIHJlcGxhY2VtZW50IGlzIG5vdGhpbmcsIHNvIHRoYXQgd2lsbCByZW1vdmUgd2hpdGUgc3BhY2VzDQoNCmxhcnZhbEFidW5kYW5jZSR0aW1lIDwtIGdzdWIocGF0dGVybiA9ICIgIixyZXBsYWNlbWVudCA9ICIiLHggPSBsYXJ2YWxBYnVuZGFuY2UkdGltZSkNCg0KYGBgDQoNCkZlZWwgdGhlIHBvd2VyISBUaGUgYGdzdWIoKWAgZnVuY3Rpb24gaXMgdmVyeSBwb3dlcmZ1bCBhbmQgdGhlIHBhdHRlcm4gbWF0Y2hpbmcgd29ya3MgYmFzZWQgb24gJ3JlZ3VsYXIgZXhwcmVzc2lvbnMnICh3aGljaCBpcyBhIG5lYXJseSB1bml2ZXJzYWwgcGF0dGVybiBtYXRjaGluZyBsYW5ndWFnZS9wcm90b2NvbCkuIEZvciBleGFtcGxlLCBpZiB5b3UgaGFkIHNwYWNlcyB5b3Ugd2FudGVkIHRvIGtlZXAgYW5kIG9ubHkgd2FudGVkIHRvIHJlbW92ZSB3aGl0ZSBzcGFjZXMgYXQgdGhlIGVuZCwgeW91IGNvdWxkIHVzZSBgcGF0dGVybiA9ICIgKyQiYCBzaW5jZSB0aGUgZG9sbGFyIHNpZ24gaW4gcmVnZXggbWVhbnMgJ2VuZHMgd2l0aCcgYW5kIHRoZSBwbHVzIG1lYW5zICdvbmUgb3IgbW9yZScsIHNvIGdzdWIgd291bGQgbWF0Y2ggb25lIG9yIG1vcmUgc3BhY2VzIGF0IHRoZSBlbmQgb2YgYSBjaGFyYWN0ZXIgc3RyaW5nLiBZb3UgY2FuIHByYWN0aWNlIHlvdXIgJ3JlZ2V4JyB3aXRoIFtyZWdleHBhbF0oaHR0cDovL3d3dy5yZWdleHBhbC5jb20vKSBhbmQgdGhpcyBbY2hlYXRzaGVldF0oaHR0cDovL3d3dy5yZXhlZ2cuY29tL3JlZ2V4LXF1aWNrc3RhcnQuaHRtbCkNCg0KT3RoZXIgdXNlZnVsIHRoaW5ncyB5b3Ugc2hvdWxkIGNoZWNrIGFyZSB0aGUgbWluaW11bSBhbmQgbWF4aW11bSB2YWx1ZXMgZm9yIGVhY2ggY29sdW1uIHRvIG1ha2Ugc3VyZSB0aGluZ3Mgd2VyZSBlbnRlcmVkIGFsbCBpbiB0aGUgc2FtZSBvcmRlciBvZiBtYWduaXR1ZGUsIG9yIGEgZXZlbiBhIHF1aWNrIGhpc3RvZ3JhbQ0KDQpgYGB7cn0NCm1pbihsYXJ2YWxBYnVuZGFuY2UkTWFyZ2FyaXRlcy5zcHAuKQ0KbWF4KGxhcnZhbEFidW5kYW5jZSRNYXJnYXJpdGVzLnNwcC4pDQpoaXN0KGxhcnZhbEFidW5kYW5jZSRNYXJnYXJpdGVzLnNwcC4pDQoNCmBgYA0KDQpMYXN0bHksIHRoZXJlIGFyZSBzb21lIGNvbHVtbiBuYW1lcyB0aGF0IGFyZSBub3QgJ3VwIHRvIGNvZGUnLCBzbyB0byBhdm9pZCBhIHN0ZXJuIHRhbGtpbmcgdG8gZnJvbSBDSE9OZSdzIGRhdGEgbWFuYWdlciwgbGV0J3MgZml4IHRoYXQgbm93ISAoQWxzbywgeW91IG1heSBoYXZlIG5vdGljZWQgdGhhdCBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIHdlcmUgc3dhcHBlZCEpDQoNCmBgYHtyfQ0KbmFtZXMobGFydmFsQWJ1bmRhbmNlKQ0KDQpuYW1lcyhsYXJ2YWxBYnVuZGFuY2UpW25hbWVzKGxhcnZhbEFidW5kYW5jZSk9PSJsb25nIl0gPC0gImRlY2ltYWxMYXRpdHVkZSINCm5hbWVzKGxhcnZhbEFidW5kYW5jZSlbbmFtZXMobGFydmFsQWJ1bmRhbmNlKT09ImxhdCJdIDwtICJkZWNpbWFsTG9uZ2l0dWRlIg0KbmFtZXMobGFydmFsQWJ1bmRhbmNlKVtuYW1lcyhsYXJ2YWxBYnVuZGFuY2UpPT0ic2l0ZSJdIDwtICJsb2NhdGlvbklEIg0KDQpgYGANCg0KDQpFdmVyeXRoaW5nIG5vdyBzZWVtcyByZWFzb25hYmxlIHRvIG1lLCBidXQgaXQgaXMgeW91ciByZXNwb25zaWJpbGl0eSB0byBjaGVjayB0aGF0IGVhY2ggY29sdW1uIG9mIHlvdXIgZGF0YSAnbWFrZXMgc2Vuc2UnLiBCdXQgZm9yIHNha2Ugb2YgdGltZSwgbGV0cyBtb3ZlIG9uIQ0KDQpOb3cgd2UgaGF2ZSBhIGNob2ljZSwgd2UgY2FuIGVpdGhlciBydW4gYSBkYXRhIGNsZWFuaW5nIHNjcmlwdCBldmVyeSB0aW1lIHdlIGxvYWQgdGhlIHJhdyBkYXRhLCBvciB3ZSBjYW4gc2F2ZSBhICdjbGVhbicgZGF0YSBwcm9kdWN0LiBMZXQncyBkbyB0aGUgbGF0dGVyLg0KDQpgYGB7cn0NCiMgbGV0J3MgY3JlYXRlIGEgbmV3IGZvbGRlciBmb3IgaW50ZXJtZWRpYXRlIGRhdGEgcHJvZHVjdHMNCmRpci5jcmVhdGUoImRhdGEiKQ0KDQojIFRoZW4gbGV0J3Mgc2F2ZSB0aGUgY2xlYW5lZCBkYXRhIGluIHRoYXQgZm9sZGVyDQp3cml0ZS5jc3YobGFydmFsQWJ1bmRhbmNlLCBmaWxlID0gImRhdGEvbGFydmFsQWJ1bmRhbmNlQ2xlYW4uY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KPioqUHJvLXRpcCoqDQo+DQo+IE5vdGljZSB0aGF0Og0KPiAtIHdlIGFyZSBub3Qgd3JpdGluZyBvdmVyIHRoZSByYXcgZGF0YQ0KPiAtIHdlIGFyZSBub3Qgd3JpdGluZyBpbiB0aGUgc2FtZSBmb2xkZXIgYXMgdGhlIHJhdyBkYXRhDQo+IC0gd2UgYXJlIG5hbWluZyBvdXIgbmV3IGRhdGEgZmlsZSBpbmZvcm1hdGl2ZWx5DQoNCiMjIE1ha2luZyB5b3VyIG93biBmdW5jdGlvbnMgYW5kIHBhY2thZ2VzDQoNClBhcnQgb2YgUidzIGF3ZXNvbWVuZXNzIGlzIHRoYXQgaXQgYWxyZWFkeSBjb21lcyB3aXRoIGEgbG90IG9mIGZ1bmN0aW9ucyB0aGF0IGFyZSB2ZXJ5IHVzZWZ1bCBmb3IgZXZlcnlkYXkgc2NpZW5jZS4gQWRkaXRpb25hbGx5LCB0aGVyZSBhcmUgbWFueSBgcGFja2FnZXNgIHdoaWNoIGFyZSBlc3NlbnRpYWxseSBjb2xsZWN0aW9ucyBvZiBuZXcgZnVuY3Rpb25zIGFuZCBoZWxwIGZpbGVzIGdlbmVyYXRlZCBieSB1c2VycyBsaWtlIHlvdSBhbmQgbWUgdGhhdCBhZGQgdG8gdGhlIGFscmVhZHkgYnJvYWQgZnVuY3Rpb25hbGl0eSBvZiBSLg0KDQoqV2FybmluZzogc2hhbWVsZXNzIHNlbGYgcHJvbW90aW9uIGJlbG93ISoNCg0KSGVyZSBpcyBhIHBhY2thZ2UgSSBjcmVhdGVkIGNhbGxlZCBbQkVTVE1QQV0oaHR0cHM6Ly9naXRodWIuY29tL3JlbWktZGFpZ2xlL0JFU1RNUEEpIGFuZCB0aGUgcGVlci1yZXZpZXdlZCBwYXBlciBkZXNjcmliaW5nIGl0OiBbQW4gYWRhcHRhYmxlIHRvb2xraXQgdG8gYXNzZXNzIGNvbW1lcmNpYWwgZmlzaGVyeSBjb3N0cyBhbmQgYmVuZWZpdHMgcmVsYXRlZCB0byBtYXJpbmUgcHJvdGVjdGVkIGFyZWEgbmV0d29yayBkZXNpZ25dKGh0dHBzOi8vZjEwMDByZXNlYXJjaC5jb20vYXJ0aWNsZXMvNC0xMjM0L3YyKQ0KDQpNYWtpbmcgeW91ciBvd24gZnVuY3Rpb24gZm9sbG93cyB0aGUgcGFydGljdWxhciBmb3JtYXQgYmVsb3csIGxldCdzIG1ha2Ugb25lIGNhbGxlZCBgY3VzdG9tbWVhbigpYA0KYGBge3J9DQoNCmN1c3RvbW1lYW4gPC0gZnVuY3Rpb24oeCl7DQogICAgbSA8LSBzdW0oeCkvbGVuZ3RoKHgpDQogICAgcmV0dXJuKG0pDQp9DQoNCiMgc28gd2UgZGVmaW5lZCBjdXN0b21tZWFuIGFzIGEgZnVuY3Rpb24gd2l0aCBhcmd1bWVudHMgJ3gnLCBhbmQgaXQgZG9lcyB3aGF0IGlzIGluc2lkZSB0aGUgY3VybHkgYnJhY2tldHMNCiMgaXQgd2lsbCByZXR1cm4gbSB3aGljaCBpcyB0aGUgbWVhbiBvZiB4Lg0KDQp4IDwtIGMoMSwyLDMsNikNCg0KIyBkb2VzIGl0IHdvcms/DQpjdXN0b21tZWFuKHggPSB4KQ0KYGBgDQoNCklmIHlvdSBtYWtlIGEgZmV3IG9mIHRob3NlIGFuZCB3cml0ZSBzb21lIGhlbHAgZmlsZXMgdG8gZ28gYWxvbmcgd2l0aCB0aGVtLCB5b3UgY2FuIG1ha2UgeW91ciBvd24gcGFja2FnZS4gSWYgeW91J3JlIGludGVyZXN0ZWQgc2VlIHRoZSAiW1IgcGFja2FnZXNdKGh0dHA6Ly9yLXBrZ3MuaGFkLmNvLm56LykiIGJvb2sgYnkgSGFkbGV5IFdpY2toYW0gKENoaWVmIFNjaWVudGlzdCBhdCBSU3R1ZGlvLCBub3QgdGhlIGxhc3QgdGltZSBJIHdpbGwgbWVudGlvbiBoaW0pLCBidXQgbWFraW5nIHBhY2thZ2VzIGlzIGJleW9uZCB0aGUgc2NvcGUgb2Ygd2hhdCBJIGNhbiBjb3ZlciBpbiB0aGlzIHdvcmtzaG9wDQoNCkFueXdheSwgdGhlcmUgaXMgYSBjZW50cmFsIG9yZ2FuaXphdGlvbiBjYWxsZWQgJ0NvbXByZWhlbnNpdmUgUiBBcmNoaXZlIE5ldHdvcmsnIG9yIENSQU4gd2hpY2ggaG91c2VzIGFsbCB0aGUgb2ZmaWNpYWwgcGFja2FnZSwgYnV0IHRoZXJlIGFyZSBhbHNvIG90aGVyIHBhY2thZ2VzIChsaWtlIG1pbmUpLCBhcyB3ZWxsIGFzIHRoZSBkZXZlbG9wbWVudCB2ZXJzaW9uIG9mIG1hbnkgb2YgdGhlIG9mZmljaWFsIHBhY2thZ2VzIG9uIGdpdGh1YiB0aGF0IGFyZSB3b3J0aCB0YWtpbmcgYSBsb29rIGF0Lg0KDQojIyBJbnN0YWxsIHBhY2thZ2VzIGZvciB0b21vcnJvdw0KVG8gaW5zdGFsbCBwYWNrYWdlcywgeW91IGNhbiBlaXRoZXIgdXNlIHRoZSBQYWNrYWdlcyB3aW5kb3cgYXQgdGhlIGJvdHRvbSByaWdodCwgb3IgeW91IGNhbiBkbyBpdCB3aXRoIHdyaXR0ZW4gY29tbWFuZHMgKG15IHByZWZlcmVuY2UpLiBIZXJlIGFyZSBhIGZldyB3ZSB3aWxsIHVzZSB0b21vcnJvdywgdHJ5IGluc3RhbGxpbmcgdGhlbSBub3cgYW5kIGxldCB1cyBrbm93IGlmIHlvdSBnZXQgYW55IGVycm9ycy4NCg0KYGBge3IsIGV2YWw9RkFMU0V9DQoNCmluc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpICMgVGhlIHRpZHl2ZXJzZSBpcyBhIGNvbGxlY3Rpb24gb2YgUiBwYWNrYWdlcyB0aGF0IHNoYXJlIGNvbW1vbiBwaGlsb3NvcGhpZXMgYW5kIGFyZSBkZXNpZ25lZCB0byB3b3JrIHRvZ2V0aGVyLiAoZS5nLiBnZ3Bsb3QyLCBkcGx5ciwgdGlkeXIpDQoNCmluc3RhbGwucGFja2FnZXMoJ21hcm1hcCcpICMgSW1wb3J0IHh5eiBkYXRhIGZyb20gdGhlIE5PQUEgKE5hdGlvbmFsIE9jZWFuaWMgYW5kIEF0bW9zcGhlcmljIEFkbWluaXN0cmF0aW9uLCA8aHR0cDovL3d3dy5ub2FhLmdvdj4pLCBHRUJDTyAoR2VuZXJhbCBCYXRoeW1ldHJpYyBDaGFydCBvZiB0aGUgT2NlYW5zLCA8aHR0cDovL3d3dy5nZWJjby5uZXQ+KSBhbmQgb3RoZXIgc291cmNlcywgcGxvdCB4eXogZGF0YSB0byBwcmVwYXJlIHB1YmxpY2F0aW9uLXJlYWR5IGZpZ3VyZXMNCg0KaW5zdGFsbC5wYWNrYWdlcygncmFzdGVyJykgIyBSZWFkaW5nLCB3cml0aW5nLCBtYW5pcHVsYXRpbmcsIGFuYWx5emluZyBhbmQgbW9kZWxpbmcgb2YgZ3JpZGRlZCBzcGF0aWFsIGRhdGEgKGFuZCBhbHNvIGdldHRpbmcgYWNjZXNzIHRvIEdBRE0gYmFzZW1hcHMpDQoNCmluc3RhbGwucGFja2FnZXMoJ2RldnRvb2xzJykgIyBBbGxvd3MgeW91IHRvIGluc3RhbGwgcGFja2FnZXMgZnJvbSBnaXRodWINCg0KIyB0aGUgJ3JvYmlzJyBwYWNrYWdlIG9uIENSQU4gZG9lcyBub3Qgd29yayB3aXRoIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiBSICh5ZXQpLCBzbyB3ZSBuZWVkIHRvIGdldCB0aGUgbGF0ZXN0IHZlcnNpb24gZnJvbSBnaXRodWINCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaW9iaXMvcm9iaXMiKQ0KDQojIHlvdSBhbHJlYWR5IGhhdmUgdGhlIENSQU4gdmVyc2lvbiBvZiBnZ3Bsb3QyLCBidXQgdGhhdCB2ZXJzaW9uIGlzIG5vdCBjb21wYXRpYmxlIHdpdGggdGhlIHNmIHBhY2thZ2UgeWV0DQojIFNvLCB3ZSBuZWVkIHRoZSBnaXRodWIgdmVyc2lvbiBvZiBnZ3Bsb3QyIGFzIHdlbGwhDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInRpZHl2ZXJzZS9nZ3Bsb3QyIikNCg0KaW5zdGFsbC5wYWNrYWdlcygnZ3JpZEV4dHJhJykgIyB0byBiZSBhYmxlIHRvIGFycmFuZ2UgbXVsdGlwbGUgZ2dwbG90IHBsb3RzDQoNCmluc3RhbGwucGFja2FnZXMoJ3RheGl6ZScpICMgVG8gZXh0cmFjdCBhbmQgdmFsaWRhdGUgc3BlY2llcyB0YXhvbm9teQ0KDQppbnN0YWxsLnBhY2thZ2VzKCdyZmlzaGJhc2UnKSAjIFRvIGFjY2VzcyByZXNvdXJjZXMgYXZhaWxhYmxlIG9uIEZpc2hiYXNlIGFuZCBTZWFMaWZlQmFzZQ0KDQppbnN0YWxsLnBhY2thZ2VzKCdyZ2xvYmknKSAjIFRvIGFjY2VzcyBpbnRlcmFjdGlvbnMgZGF0YQ0KDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInJvcGVuc2NpL3Jub2FhIikgIyBUbyBhY2Nlc3MgZW52aXJvbm1lbnRhbCBkYXRhIGZyb20gdGhlIE5PQUEgZGF0YWJhc2VzDQoNCmluc3RhbGwucGFja2FnZXMoJ2tuaXRyJykNCg0KaW5zdGFsbC5wYWNrYWdlcygnYmlvbW9kMicpICMgdG8gcGVyZm9ybSBzcGVjaWVzIGRpc3RyaWJ1dGlvbiBtb2RlbHMNCg0KaW5zdGFsbC5wYWNrYWdlcygnaUdyYXBoJykgIyB0byBwcm9kdWNlIG5ldHdvcmsgcGxvdHMNCg0KaW5zdGFsbC5wYWNrYWdlcygnbmV0d29ya0QzJykgIyB0byBwcm9kdWNlIGh0bWwgKGEuay5hLiBpbnRlcmFjdGl2ZSkgbmV0d29yayBwbG90cyENCg0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJndWlibGFuY2hldC9ITVNDIikgIyBwYWNrYWdlIHRvIHBlcmZvcm0gaGllcmFyY2hpY2FsIG1vZGVsaW5nIG9mIHNwZWNpZXMgY29tbXVuaXRpZXMNCg0KaW5zdGFsbC5wYWNrYWdlcygnY29kYScpICMgcGFja2FnZSB0byBzdW1tYXJpemUgYW5kIHBsb3Qgb3V0cHV0cyBmcm9tIE1hcmtvdiBDaGFpbnMNCg0KaW5zdGFsbC5wYWNrYWdlcygnY29ycnBsb3QnKSAjIHZpc3VhbGl6YXRpb24gb2YgY29ycmVsYXRpb24gbWF0cmljZXMNCg0KaW5zdGFsbC5wYWNrYWdlcygnY2lyY2xpemUnKSAjIGNpcmN1bGFyIHZpc3VhbGl6YXRpb24gb2YgZGF0YQ0KDQppbnN0YWxsLnBhY2thZ2VzKCdNb2RlbE1ldHJpY3MnKSAjIGNvbGxlY3Rpb24gb2YgbWV0cmljcyBjb2RlZCBmb3IgZWZmaWNpZW5jeSBpbiBDKysgdXNpbmcgUmNwcA0KDQppbnN0YWxsLnBhY2thZ2VzKCJwZGZ0b29scyIpICMgdG8gZXh0cmFjdCBwZGYgY29udGVudA0KDQppbnN0YWxsLnBhY2thZ2VzKCdzdHJpbmdyJykgIyBTaW1wbGUsIENvbnNpc3RlbnQgV3JhcHBlcnMgZm9yIENvbW1vbiBTdHJpbmcgT3BlcmF0aW9ucw0KDQppbnN0YWxsLnBhY2thZ2VzKCd0aWR5dGV4dCcpICMgdGV4dCBhbmFseXNpcyBwYWNrYWdlDQoNCmluc3RhbGwucGFja2FnZXMoJ3ZpcmlkaXMnKSAjIFBvcnQgb2YgdGhlIG5ldyAnbWF0cGxvdGxpYicgY29sb3IgbWFwcw0KDQppbnN0YWxsLnBhY2thZ2VzKCd0aWJibGUnKSAjIFNpbXBsZSBEYXRhIEZyYW1lcw0KDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImRncnR3by93aWR5ciIpICMgV2lkZW4sIHByb2Nlc3MsIGFuZCByZS10aWR5IGEgZGF0YXNldA0KDQppbnN0YWxsLnBhY2thZ2VzKCdnZ3JhcGgnKSAjIEFuIEltcGxlbWVudGF0aW9uIG9mIEdyYW1tYXIgb2YgR3JhcGhpY3MgZm9yIEdyYXBocyBhbmQgTmV0d29ya3MNCg0KaW5zdGFsbC5wYWNrYWdlcygnd29yZGNsb3VkMicpICMgd29yZGxlIGdlbmVyYXRvcg0KDQppbnN0YWxsLnBhY2thZ2VzKCdsZWFmbGV0JykgIyBDcmVhdGUgYW5kIGN1c3RvbWl6ZSBpbnRlcmFjdGl2ZSBtYXBzDQoNCmluc3RhbGwucGFja2FnZXMoJ21hcHZpZXcnKSAjIEludGVyYWN0aXZlIFZpZXdpbmcgb2YgU3BhdGlhbCBPYmplY3RzIGluIFINCg0KaW5zdGFsbC5wYWNrYWdlcygnc2NhbGVzJykgIyBHcmFwaGljYWwgc2NhbGVzIG1hcCBkYXRhIHRvIGFlc3RoZXRpY3MNCg0KDQojIHRvIGdhaW4gYWNjZXNzIHRvIHRoZSBmdW5jdGlvbnMgaW4gYSBwYWNrYWdlLCB5b3UgY2FuIHVzZSBsaWJyYXJ5KCksIGVnOg0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KIyMgS2VlcGluZyBSIHVwIHRvIGRhdGUNCg0KRXZlcnkgc28gb2Z0ZW4sIFIgcmVsZWFzZXMgYSBuZXcgdXBkYXRlLiBNYW55IG9mIHlvdSBoYWQgaW5zdGFsbCBpc3N1ZXMgbGFzdCBuaWdodCB0aGF0IHdlcmUgcmVzb2x2ZWQgYnkgaW5zdGFsbGluZyB0aGUgbmV3ZXN0IHZlcnNpb24gb2YgUi4gVG8ga2VlcCB0aGluZ3MgcnVubmluZyBzbW9vdGhseSBpbiB0aGUgZnV0dXJlLCBoZXJlIGlzIGEgZ3JlYXQgdHJpY2s6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBpbnN0YWxsIHRoZSBwYWNrYWdlIGNhbGxlZCBpbnN0YWxscg0KaW5zdGFsbC5wYWNrYWdlcygiaW5zdGFsbHIiKQ0KDQojIGxvYWQgdGhlIGxpYnJhcnkNCmxpYnJhcnkoaW5zdGFsbHIpDQoNCiMgcnVuIHRoZSB1cGRhdGVyIGZ1bmN0aW9uICh0aGlzIGlzIGJlc3QgZG9uZSBvdXRzaWRlIFJzdHVkaW8gaW4gdGhlIFIgZ3VpKQ0KdXBkYXRlcigpDQoNCmBgYA0KDQpUaGlzIHdpbGwgcHJvbXB0IHlvdSB0byBpbnN0YWxsIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiBSIGFuZCBjb3B5IG92ZXIgYWxsIG9mIHlvdSBwYWNrYWdlcyBpZiB5b3UgY2hvb3NlIHRvIGRvIHNvICh0aGUgYWx0ZXJuYXRpdmUgaXMgaW5zdGFsbGluZyB0aGVtIGFsbCBieSBoYW5kIGFnYWluKSwgYW5kIGl0IGNhbiBhbHNvIHVwZGF0ZSB5b3VyIHBhY2thZ2VzIGZvciB5b3UuIEl0J3MgcmVhbGx5IGEgZ3JlYXQgdGltZSBzYXZlciENCg0KDQpbPDxCQUNLXShodHRwczovL3JlbWktZGFpZ2xlLmdpdGh1Yi5pby8yMDE3LUNIT05lLURhdGEvKQ0K