Using R for GIS
In this course, we will be using R for all our GIS needs. If you’ve
never used R before, no worries! We will move at a slow and steady pace
and will provide support along the way. If you’re an R pro, feel free to
flex your R skills and (hopefully) build on them!
The objectives of the guide are as follows
- Install and set up R and RStudio
- Understand R data types
- Understand R data structures
- Understand R functions
- Introduction to tidyverse and its suite of data wrangling
functions
- Understand R Markdown
- This lab guide follows closely and supplements the material
presented in Chapters 2, 4, 5, 7, and 21 in the textbook R for Data Science (RDS).
What is R?
R is a free, open source statistical programming language. It is
useful for data cleaning, analysis, and visualization. R is an
interpreted language, not a compiled one. This means that you type
something into R and it does what you tell it. It is both a command line
software and a programming environment. It is an extensible, open-source
language and computing environment for Windows, Macintosh, UNIX, and
Linux platforms, which allows for the user to freely distribute, study,
change, and improve the software. It is basically a free, super big, and
complex calculator. You will be using R to accomplish all data analysis
tasks in this class. You might be wondering “Why in the world do we need
to know how to use a statistical software program?” Here are the main
reasons:
You will be learning about new concepts in lecture and the
readings. Applying these concepts using real data is an important form
of learning. A statistical software program is the most efficient (and
in many cases the only) means of running data analyses, not just in the
cloistered setting of a university classroom, but especially in the real
world. Applied data analysis will be the way we bridge statistical
theory to the “real world.” And R is the vehicle for accomplishing
this.
In order to do applied data analysis outside of the classroom,
you need to know how to use a statistical program. There is no way
around it. If you want to collect data on health, you need a program to
store and analyze that data.
The next question you may have is “I love Excel or SAS or SPSS or
Stata [or insert your favorite program]. Why can’t I use that and forget
your stupid R?” Here are some reasons:
- R is free. Most programs are not.
- R is open source. Which means the software is community supported.
This allows you to get help not from some big corporation
(e.g. Microsoft with Excel), but people all around the world who are
using R. And R has a lot of users, which means that if
you have a problem, and you pose it to the user community, someone will
help you.
- R is powerful and extensible (meaning that procedures for analyzing
data that don’t currently exist can be readily developed);
- R has the capability for mapping data, an asset not generally
available in other statistical software.
- If it isn’t already, R is becoming the de-facto data analysis tool
in many fields, including for many CA DPH positions.
R is different from Excel in that it is generally not a
point-and-click program. You will be primarily writing code to clean and
analyze data. What does writing or sourcing code mean?
A basic example will help clarify. Let’s say you are given a dataset
with cancer cases across CA. You have a variable in the dataset
representing age Let’s say this variable is named AGE.
To get the mean age of the people in your dataset, you would write code
that would look something like this
# Download Cancer Dataset
download.file("https://raw.githubusercontent.com/pjames-ucdavis/SPH215/refs/heads/main/CA_Cancer_Data.rds", "ca_cancer.rds", mode = "wb")
# Read in Cancer Dataset
cancer <- readRDS("ca_cancer.rds")
# Get names of columns or variables
head(cancer)
## time event AGE INS geometry
## 1 1.275976 1 67 Mcr -122.3492, 38.3025
## 14 3.509907 1 69 Mcr -121.98325, 37.82052
## 17 10.297702 0 75 Mng -122.3092, 38.3314
## 36 7.012532 0 46 Mcr -122.20308, 38.09592
## 55 3.389200 0 70 Mcr -122.63560, 38.26257
## 92 6.110251 1 59 Unk -122.01982, 37.35523
# Get mean of AGE variable
mean(cancer$AGE)
## [1] 60.4692
The command tells the program to get the mean of the variable
AGE. If you wanted the sum, you write the command
sum(cancer$AGE).
Now, where do you write this command? You write it in a script. A
script is basically a text file. Think of writing code as something
similar to writing an essay in a word document. Instead of sentences to
produce an essay, in a programming script you are writing lines of code
to run a data analysis. We’ll go through scripting in more detail later
in this lab, but the basic process of sourcing code to run a data
analysis task is as follows.
- Write code. First, you open your script file, and write code or
various commands (like
mean(cancer$AGE)) that will execute
data analysis tasks in this file.
- Send code to the software program to run (R in our case).
- Program produces results based on code. The program reads in your
commands from the script and executes them, spitting out results in its
console screen.
I am skipping over many details, but the above steps outline the
general work flow. You might now be thinking that you’re perfectly happy
pointing and clicking your mouse in Excel (or wherever) to do your data
analysis tasks. So, why should you adopt the statistical programming
approach to conducting a data analysis?
- Your script documents the decisions you made during the data
analysis process. This is beneficial for many reasons.
- It allows you to recreate your steps if you need to rerun or alter
your analysis many weeks, months, or even years in the future.
- It allows you to share your steps with other people. If someone asks
you what were the decisions made in the data analysis process, - just
hand them the script.
- Related to the above points, a script promotes
transparency (here is what I did) and
reproducibility (you can do it too). When you write
code, you are forced to explicitly state the steps you took to do your
research. When you do research by clicking through drop-down menus, your
steps are lost, or at least documenting them requires considerable extra
effort.
If you make a mistake in a data analysis step, you can go back,
change a few lines of code, and poof, you’ve fixed your
problem.
It is more efficient. In particular, cleaning data can encompass
a lot of tedious work that can be streamlined using statistical
programming.
Hopefully, I’ve convinced you that statistical programming and R are
worthwhile to learn. Now let’s talk about getting R on your
computer!
Getting R
R can be downloaded from one of the “CRAN” (Comprehensive R Archive
Network) sites. In the US, the main site is at http://cran.us.r-project.org/. Look in the “Download and
Install R” area at the top. Click on the appropriate link based on your
operating system.
If you already have R on your computer, make sure you have
the most updated version of R on your personal computer (R version 4.5.2
([Not] Part in a Rumble)).
Mac OS X
On the “R for Mac OS” page, there are multiple packages that
could be downloaded. Depending on the model of your Mac, pick the
appropriate .pkg file. Note the details for some operating systems.
If you are using an older operating system, please follow
instructions.
After the package finishes downloading, locate the installer on
your hard drive, double-click on the installer package, and after a few
screens, select a destination for the installation of the R framework
(the program) and the R.app GUI. Note that you will have to supply the
Administrator’s password. Close the window when the installation is
done.
An application will appear in the Applications folder:
R.app.
Browse to the XQuartz
download page. Click on the most recent version of XQuartz to download
the application.
Run the XQuartz installer. XQuartz is needed to create windows to
display many types of R graphics: this used to be included in MacOS
until version 10.8 but now must be downloaded separately.
Windows
On the “R for Windows” page, click on the “base” link, which
should take you to the “R-4.5.2 for Windows” page
On this page, click “Download R-4.5.2 for Windows”, and save the
.exe file to your hard disk when prompted. Saving to the desktop is
fine.
To begin the installation, double-click on the downloaded file.
Don’t be alarmed if you get unknown publisher type warnings. Window’s
User Account Control will also worry about an unidentified program
wanting access to your computer. Click on “Run”.
Select the proposed options in each part of the install dialog.
When the “Select Components” screen appears, just accept the standard
choices
What is R Studio?
If you click on the R program you just downloaded, you will find a
very basic user interface. For example, below is what I get on a
Mac:
The Basic R Console
We will not use R’s direct interface to run analyses in this class.
Instead, we will use the program RStudio, which is much
easier to interact with! RStudio gives you a true integrated development
environment (IDE), where you can write code in a window, see results in
other windows, see locations of files, see objects you’ve created, and
so on. To clarify which is which: R is the name of the programming
language itself and RStudio is an interface that makes writing code,
running analyses, and visualizing data in R so much easier.
Getting R Studio
To download and install RStudio, follow the directions below
Navigate to RStudio’s download
site
We’ve already downloaded R, so click on the appropriate link to
Install RStudio based on your OS (Windows, Mac, Linux and many others).
Do not download anything from the “All Installers and Tarballs”
section.
Click on the installer that you downloaded. Follow the
installation directions, making sure to keep all defaults intact. After
installation, RStudio should pop up in your Applications or Programs
folder/menu.
The RStudio Interface
Open up RStudio. You should see the interface shown in the figure
below which has three windows.
The RStudio Console
- Console (bottom left) - The way R works is you
write a line of code to execute some kind of task on a data object. -
The R Console allows you to run code interactively. The screen prompt
> is an invitation from R to enter its - world. This is
where you type code in, press enter to execute the code, and see the
results.
- Environment, History, and Connections tabs
(upper-right)
- Environment - shows all the R objects that are
currently open in your workspace. This is the place, for example, where
you will see any data you’ve loaded into R. When you exit RStudio, R
will clear all objects in this window. You can also click on
to clear out all the objects loaded and
created in your current session.
- History - shows a list of executed commands in the
current session.
- Connections - you can connect to a variety of data
sources, and explore the objects and data inside the connection. I
typically don’t use this window, but you can.
- Files, Plots, Packages, Help and Viewer tabs
(lower-right)
- Files – shows all the files and folders in your
current working directory
- Plots – shows any charts, graphs, maps and plots
you’ve executed
- Packages – shows available R packages
- Help – displays help documentation
- Viewer – displays local web content
There is also a fourth window. But, we’ll get to this window a little
later. The assignment guidelines
also have more on this window!
Setting RStudio Defaults
While not required, I strongly suggest that you change preferences in
RStudio to never save the workspace so you always open with a clean
environment. See Ch.
8.1 of R4DS for some more background
From the Tools menu on RStudio, open the Tools menu and then
select Global Options.
If not already highlighted, click on the General button from the
left panel.
Uncheck the following Restore boxes
- Restore most recently opened project at startup
- Restore previously open source documents at startup
- Restore .RData into workspace at startup
Set Save Workspace to .RData on exit to “Never”.
Click OK at the bottom to save the changes and close the
preferences window. You may need to restart RStudio.
The reason for making these changes is that it is preferable for
reproducibility to start each R session with a clean environment. You
can restore a previous environment either by rerunning code or by
manually loading a previously saved session.
The R Studio environment is modified when you execute code from files
or from the console. If you always start fresh, you do not need to be
concerned about things not working because of something you typed in the
console, but did not save in a file.
You only need to set these preferences once.
R Data Types
Let’s now explore what R can do. R is really just a big fancy
calculator. For example, type in the following mathematical expression
in the R console (left window):
1+1
## [1] 2
Note that spacing does not matter: 1+1 will generate the
same answer as 1 + 1. Can you say hello to the
world?
"hello world"
## [1] "hello world"
Looks great! Note, we need to put quotes around it.
“hello world” is a character and R recognizes characters only if there
are quotes around it. This brings us to the topic of basic data types in
R. There are four basic data types in R: character, logical, numeric,
and factors (there are two others - complex and raw - but we won’t cover
them because they are rarely used in practice).
Characters
Characters are used to represent words or letters in R. We saw this
above with “hello world”. Character values are also known as strings.
You might think that the value "1" is a number. Well, if
you put quotes around, it isn’t! Anything with quotes will be
interpreted as a character. No ifs, ands or buts about it.
Logicals
A logical takes on two values: FALSE or
TRUE. Logicals are usually constructed with comparison
operators, which we’ll go through more carefully in Lab 2. Think of a
logical as the answer to a question like “Is this value greater than
(lower than/equal to) this other value?” The answer will be either
TRUE or FALSE. TRUE and
FALSE are logical values in R. For example, typing in the
following
3 > 2
## [1] TRUE
This gives you a TRUE What about the following?
"declan" == "catherine"
## [1] FALSE
Numeric
Numerics are separated into two types: integer and double. The
distinction between integers and doubles is usually not important. R
treats numerics as doubles by default because it is a less restrictive
data type. You can do any mathematical operation on numeric values. We
added one and one above. We can also multiply using the *
operator.
2*3
## [1] 6
And divide
2/3
## [1] 0.6666667
And take logs
log(1)
## [1] 0
log(0)
## [1] -Inf
Hold up! What is -Inf? Well, you can’t take the
logarithm of 0, so R is telling you that you’re getting a non numeric
value in return. The value -Inf is another value type that
you can get in R.
Factors
Think of a factor as a categorical variable. It is sort of like a
character, but not really. It is actually a numeric code with
character-valued levels. Think of a character as a true string and a
factor as a set of categories represented as characters. We won’t use
factors too much in this course, so maybe don’t worry about it for
now!
R Data Structures
You just learned that R has four basic data types. Now, let’s go
through how we can store data in R. That is, you type in the character
“hello world” or the number 3, and you want to store these values. You
do this by using R’s various data structures.
Vectors
A vector is the most common and basic R data structure and is pretty
much the workhorse of the language. A vector is simply a sequence of
values which can be of any data type but all of the same type. There are
a number of ways to create a vector depending on the data type, but the
most common is to insert the data you want to save in a vector into the
command c(). For example, to represent the values 4, 16,
and 9 in a vector type in
c(4, 16, 9)
## [1] 4 16 9
You can also have a vector of character values
c("catherine", "declan", "gwen")
## [1] "catherine" "declan" "gwen"
The above code does not actually “save” the values 4, 16, and 9 or
catherine, declan, gwen – it just presents it on the screen in a vector.
If you want to use these values again without having to type out
c(4, 16, 9), you can save it in a data
object. At the heart of almost everything you will do
(or are ever likely to do) in R is the concept that everything in R is
an object. These objects can be almost anything, from a single number or
character string (like a word) to highly complex structures like the
output of a plot, a map, a summary of your statistical analysis or a set
of R commands that perform a specific task.
You assign data to an object using the arrow sign <-.
This will create an object in R’s memory that can be called back into
the command window at any time. For example, you can save “hello world”
to a vector called b by typing in
b <- "hello world"
b
## [1] "hello world"
You can pronounce the above as “b becomes ‘hello world’”.
The first line tells R to store b as ‘hello world.’ In the next line,
we are telling R to print what b is.
Note that R is case sensitive, if you type in
B instead of b, you will get an error.
Similarly, you can save the numbers 4, 16 and 9 into a vector called
v1.
v1 <- c(4, 16, 9)
v1
## [1] 4 16 9
You should see the objects b and v1 pop up in the
Environment tab on the top right window of your RStudio interface.
Environment Window
Note that the name v1 is nothing special here. You could
have named the object x or sph215 or your pet’s name
(mine was Ali Baba). You can’t, however, name objects using special
characters (e.g. !, @, $) or only numbers (although you can combine
numbers and letters, but a number cannot be at the beginning
e.g. 2d2). For example, you’ll get an error if you save the
vector c(4,16,9) to an object with the following names
123 <- c(4, 16, 9)
!!! <- c(4, 16, 9)
## Error: <text>:2:5: unexpected assignment
## 1: 123 <- c(4, 16, 9)
## 2: !!! <-
## ^
Also note that to distinguish a character value from a variable name,
it needs to be quoted. “v1” is a character value whereas v1
is a variable. One of the most common mistakes for beginners is to
forget the quotes.
james
## ## Error in eval(expr, envir, enclos): object 'james' not found
The error occurs because R tries to print the value of object
james, but there is no such variable. So remember that any time
you get the error message object 'something' not found, the
most likely reason is that you forgot to quote a character value. If
not, it probably means that you have misspelled, or not yet created, the
object that you are referring to. We’ve included the common pitfalls and
R tips in this class resource.
Every vector has two key properties: type and
length. The type property indicates the data type that the
vector is holding. Use the command typeof() to determine
the type.
typeof(b)
## [1] "character"
typeof(v1)
## [1] "double"
Note that a vector cannot hold values of different types. If
different data types exist, R will coerce the values into the highest
type based on its internal hierarchy: logical < integer < double
< character. Type in test <- c("r", 6, TRUE) in your
R console. What is the vector type of test?
The command length() determines the number of data
values that the vector is storing.
length(b)
## [1] 1
length(v1)
## [1] 3
You can also directly determine if a vector is of a specific data
type by using the command is.X() where you replace
X with the data type. For example, to find out if
v1 is numeric, type in:
is.numeric(b)
## [1] FALSE
is.numeric(v1)
## [1] TRUE
There is also is.logical(), is.character(),
and is.factor(). You can also coerce a vector of one data
type to another. For example, save the value “1” and “2” (both in
quotes) into a vector named x1.
x1 <- c("1", "2")
typeof(x1)
## [1] "character"
To convert x1 into a numeric, use the command
as.numeric()
x2 <- as.numeric(x1)
typeof(x2)
## [1] "double"
There is also as.logical(), as.character(),
and as.factor().
An important practice you should adopt early is to keep only
necessary objects in your current R Environment. For example, we will
not be using x2 any longer in this guide. To remove this object
from R forever, use the command rm()
rm(x2)
The data frame object x2 should have disappeared from the
Environment tab. Au revoir!
Also note that when you close down R Studio, the objects you created
above will disappear for good. Unless you save them onto your hard drive
(we’ll touch on saving data in later labs), all data objects you create
in your current R session will go bye bye when you exit the program.
Data Frames
We learned that data values can be stored in data structures known as
vectors. The next step is to learn how to store vectors into an even
higher level data structure. The data frame can do this. Data frames
store vectors of the same length. Create a vector called v2 storing the
values 5, 12, and 25.
v2 <- c(5,12,25)
We can create a data frame using the command
data.frame() storing the vectors v1 and
v2 as columns.
data.frame(v1,v2)
## v1 v2
## 1 4 5
## 2 16 12
## 3 9 25
Store this data frame in an object called df1
df1<-data.frame(v1, v2)
df1 should pop up in your Environment window. You’ll notice a
next to df1. This
tells you that df1 possesses or holds more than one object.
Click on
and you’ll see
the two vectors we saved into df1. Another nice thing you can
do is directly click on df1 from the Environment window to
bring up an Excel style worksheet on the top left of your RStudio
interface. You can also type in:
View(df1)
to bring the worksheet up. You can’t edit this worksheet directly,
but it allows you to see the values that a higher level R data object
contains.
We can store different types of vectors in a data frame. For example,
we can store one character vector and one numeric vector in a single
data frame.
v3 <- c("catherine", "declan", "gwen")
df2 <- data.frame(v1, v3)
df2
## v1 v3
## 1 4 catherine
## 2 16 declan
## 3 9 gwen
For higher level data structures like a data frame, use the function
class() to figure out what kind of object you’re working
with.
class(df2)
## [1] "data.frame"
We can’t use length() on a data frame because it has
more than one vector. Instead, it has dimensions - the number
of rows and columns. You can find the number of rows and columns that a
data frame has by using the command dim()
dim(df1)
## [1] 3 2
Here, the data frame df1 has 3 rows and 2 columns. Data
frames also have column names, which are characters.
We can figure out the names of the columns using
colnames.
colnames(df1)
## [1] "v1" "v2"
In this case, the data frame used the vector names for the column
names.
We can extract columns from data frames by referring to their names
using the $ sign.
df1$v1
## [1] 4 16 9
We can also extra data from data frames using brackets [ , ]
df1[,1]
## [1] 4 16 9
The value before the comma indicates the row, which you leave empty
if you are not selecting by row, which we did above. The value after the
comma indicates the column, which you leave empty if you are not
selecting by column. The above line of code selected the first
column.
Let’s now select the 2nd row.
df1[2,]
## v1 v2
## 2 16 12
OK, so that wasn’t too hard. Now let’s try something a little
trickier! What is the value in the 2nd row and 1st column?
df1[2,1]
## [1] 16
See – we can do hard things!
Functions
Let’s take a step back and talk about functions (also known as
commands or macros (in SAS)). An R function is a packaged recipe that
converts one or more inputs (called arguments) into a single output. You
execute all of your tasks in R using functions. We have already used a
couple of functions above including typeof() and
colnames(). Every function in R will have the following
basic format
functionName(arg1 = val1, arg2 = val2, ...)
In R, you type in the function’s name and set a number of options or
parameters within parentheses that are separated by commas. Some options
need to be set by the user - i.e. the function will spit out an error
because a required option is blank - whereas others can be set but are
not required because there is a default value established.
Let’s use the function seq() which makes regular
sequences of numbers. You can find out what the options are for a
function by calling up its help documentation by typing ?
and the function name
? seq
## starting httpd help server ... done
The help documentation should pop up in the bottom right window of
your RStudio interface. The documentation should also provide some
examples of the function at the bottom of the page. Type the arguments
from = 1, to = 10 inside the parentheses.
seq(from = 1, to = 10)
## [1] 1 2 3 4 5 6 7 8 9 10
You should get the same result if you type in:
seq(1, 10)
## [1] 1 2 3 4 5 6 7 8 9 10
The code above demonstrates something about how R resolves function
arguments. When you use a function, you can always specify all the
arguments in arg = value form. But if you do not, R
attempts to resolve by position. So in the code above, it is assumed
that we want a sequence from = 1 that goes
to = 10 because we typed 1 before 10. Type in 10 before 1
and see what happens. Since we didn’t specify step size, the default
value of by in the function definition is used, which ends
up being 1 in this case.
Packages
Functions do not exist in a vacuum, but exist within R packages. Packages are the
fundamental units of reproducible R code. They include reusable R
functions, the documentation that describes how to use them, and sample
data. At the top left of a function’s help documentation, you’ll find in
curly brackets the R package that the function is housed in. For
example, type in your console ? seq. At the top right of
the help documentation, you’ll find that seq() is in the
package base. All the functions we have used so far are
part of packages that have been pre-installed and pre-loaded into R.
In order to use functions in a new package, you first need to install
the package using the install.packages() command. For
example, we will be using commands from the package
tidyverse in this lab.
options(repos = c(CRAN = "https://cloud.r-project.org"))
install.packages("tidyverse")
## Installing package into 'C:/Users/skpar/AppData/Local/R/win-library/4.5'
## (as 'lib' is unspecified)
## package 'tidyverse' successfully unpacked and MD5 sums checked
##
## The downloaded binary packages are in
## C:\Users\skpar\AppData\Local\Temp\RtmpeU3eRu\downloaded_packages
You should see a bunch of gibberish roll through your console screen.
Don’t worry, that’s just R downloading all of the other packages and
applications that tidyverse relies on. These are known
as dependencies.
Unless you get a message in red that indicates there is an error (like
we saw when we typed in “hello world” without quotes), you should be
fine.
Next, you will need to load packages in your working environment
(every time you start RStudio). We do this with the
library() function. Notice there are no quotes around
tidyverse this time (just to make things trickier for
us!).
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.6
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.1 ✔ tibble 3.3.0
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.2
## ✔ purrr 1.2.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
The Packages window at the lower-right of your RStudio shows you all
the packages you currently have installed. If you don’t have a package
listed in this window, you’ll need to use the
install.packages() function to install it. If the package
is checked, that means it is loaded into your current R session
For example, here is a section of my Packages window 
The only packages loaded into my current session is
methods, a package that is loaded every time you open
an R session. Let’s say I use install.packages() to install
the package matrixStats. The window now looks like:

Let’s load matrixStats using library(),
and then we will see a check mark appears next to
matrixStats.
install.packages("matrixStats")
## Installing package into 'C:/Users/skpar/AppData/Local/R/win-library/4.5'
## (as 'lib' is unspecified)
## package 'matrixStats' successfully unpacked and MD5 sums checked
##
## The downloaded binary packages are in
## C:\Users\skpar\AppData\Local\Temp\RtmpeU3eRu\downloaded_packages
library(matrixStats)
##
## Attaching package: 'matrixStats'
## The following object is masked from 'package:dplyr':
##
## count
Look at us!
To uninstall a package, use the function
remove.packages().
Note that you only need to install packages once with
install.packages(), but you need to load them each time you
relaunch RStudio with library(). Repeat after me:
Install once, library every time. If you need to reinstall R or update
to a new version of R, you will need to reinstall all packages. And as
noted earlier, R has several packages already preloaded into your
working environment. These are known as base packages
and a list of their functions can be found here.
Tidyverse
In most labs, we will be using commands from the
tidyverse package. Tidyverse is a collection of
high-powered, consistent, and easy-to-use packages developed by a number
of thoughtful and talented R developers. The consistency of the
tidyverse, together with the goal of increasing
productivity, mean that the syntax of tidy functions is typically
straightforward to learn. You can read more about
tidyverse principles in Chapter 9, pages 147-151 in
RDS.
Excited about entering the tidyverse? I bet you are, so here is a
badge to show your excitement!
Your Tidyverse Badge
Tibbles
Although the tidyverse works with all data objects,
its fundamental object type is the tibble. Tibbles are not only a super
fun word to say, they are data frames that tweak some older behaviors to
make life a little easier. There are two main differences in the usage
of a data frame vs a tibble: printing and subsetting. Let’s be clear
here – tibbles are just a special kind of data frame. They just make
things “tidier.” Let’s bring in some data to illustrate the differences
and similarities between data frames and tibbles. Install the package
nycflights13
install.packages("nycflights13")
## Installing package into 'C:/Users/skpar/AppData/Local/R/win-library/4.5'
## (as 'lib' is unspecified)
## package 'nycflights13' successfully unpacked and MD5 sums checked
##
## The downloaded binary packages are in
## C:\Users\skpar\AppData\Local\Temp\RtmpeU3eRu\downloaded_packages
Make sure you also load the package.
library(nycflights13)
If you look in the upper right hand Environment tab and
click on Global Environment, you will see there is a dataset
called flights included in this package. It includes
information on all 336,776 flights that departed from New York City in
2013. Let’s save this file in the local R environment.
nyctibble <- flights
class(nyctibble)
## [1] "tbl_df" "tbl" "data.frame"
This dataset is a tibble. Let’s also save it as a regular data frame
by using the as.data.frame() function.
nycdf <- as.data.frame(flights)
class(nycdf)
## [1] "data.frame"
The first difference between data frames and tibbles is how the
dataset looks. Tibbles have a refined print method that shows only the
first 10 rows, and only the columns that fit on the screen. In addition,
each column reports its name and type.
nyctibble
## # A tibble: 336,776 × 19
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
## <int> <int> <int> <int> <int> <dbl> <int> <int>
## 1 2013 1 1 517 515 2 830 819
## 2 2013 1 1 533 529 4 850 830
## 3 2013 1 1 542 540 2 923 850
## 4 2013 1 1 544 545 -1 1004 1022
## 5 2013 1 1 554 600 -6 812 837
## 6 2013 1 1 554 558 -4 740 728
## 7 2013 1 1 555 600 -5 913 854
## 8 2013 1 1 557 600 -3 709 723
## 9 2013 1 1 557 600 -3 838 846
## 10 2013 1 1 558 600 -2 753 745
## # ℹ 336,766 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## # hour <dbl>, minute <dbl>, time_hour <dttm>
Tibbles are designed so that you don’t overwhelm your console when
you print large data frames. Compare the print output above to what you
get with a data frame.
nycdf
Um, that was a lot….Tibble is much cleaner. You can bring up the
Excel like worksheet of the tibble (or data frame) using the
View() function.
View(nyctibble)
You can identify the names of the columns (and hence the variables in
the dataset) by using the function names().
names(nyctibble)
## [1] "year" "month" "day" "dep_time"
## [5] "sched_dep_time" "dep_delay" "arr_time" "sched_arr_time"
## [9] "arr_delay" "carrier" "flight" "tailnum"
## [13] "origin" "dest" "air_time" "distance"
## [17] "hour" "minute" "time_hour"
Those may come in handy if we wanted to analyze the data!
Finally, let’s convert a regular data frame to a tibble using the
as_tibble() function.
as_tibble(nycdf)
## # A tibble: 336,776 × 19
## year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
## <int> <int> <int> <int> <int> <dbl> <int> <int>
## 1 2013 1 1 517 515 2 830 819
## 2 2013 1 1 533 529 4 850 830
## 3 2013 1 1 542 540 2 923 850
## 4 2013 1 1 544 545 -1 1004 1022
## 5 2013 1 1 554 600 -6 812 837
## 6 2013 1 1 554 558 -4 740 728
## 7 2013 1 1 555 600 -5 913 854
## 8 2013 1 1 557 600 -3 709 723
## 9 2013 1 1 557 600 -3 838 846
## 10 2013 1 1 558 600 -2 753 745
## # ℹ 336,766 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## # tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## # hour <dbl>, minute <dbl>, time_hour <dttm>
Not all functions work with tibbles, particularly those that are
specific to spatial data. As such, we’ll be using a combination of
tibbles and regular data frames throughout the class, with a preference
towards tibbles where possible. Note that when you search on Google for
how to do something in R, you will likely get non-tidy
ways of doing things. Most of these suggestions are fine, but
some are not and may screw you up down the road. My advice is to try to
stick with tidy functions to do things in R.
Anyway, you earned another badge. Yes!
Your Tibble Badge
LS0tDQp0aXRsZTogJ0xhYiAxOiBJbnRybyB0byBSJw0KLS0tDQoNClwNCg0KIyBVc2luZyBSIGZvciBHSVMNCg0KSW4gdGhpcyBjb3Vyc2UsIHdlIHdpbGwgYmUgdXNpbmcgUiBmb3IgYWxsIG91ciBHSVMgbmVlZHMuIElmIHlvdSd2ZSBuZXZlciB1c2VkIFIgYmVmb3JlLCBubyB3b3JyaWVzISBXZSB3aWxsIG1vdmUgYXQgYSBzbG93IGFuZCBzdGVhZHkgcGFjZSBhbmQgd2lsbCBwcm92aWRlIHN1cHBvcnQgYWxvbmcgdGhlIHdheS4gSWYgeW91J3JlIGFuIFIgcHJvLCBmZWVsIGZyZWUgdG8gZmxleCB5b3VyIFIgc2tpbGxzIGFuZCAoaG9wZWZ1bGx5KSBidWlsZCBvbiB0aGVtISANCg0KVGhlIG9iamVjdGl2ZXMgb2YgdGhlIGd1aWRlIGFyZSBhcyBmb2xsb3dzDQoNCi0gSW5zdGFsbCBhbmQgc2V0IHVwIFIgYW5kIFJTdHVkaW8NCi0gVW5kZXJzdGFuZCBSIGRhdGEgdHlwZXMNCi0gVW5kZXJzdGFuZCBSIGRhdGEgc3RydWN0dXJlcw0KLSBVbmRlcnN0YW5kIFIgZnVuY3Rpb25zDQotIEludHJvZHVjdGlvbiB0byB0aWR5dmVyc2UgYW5kIGl0cyBzdWl0ZSBvZiBkYXRhIHdyYW5nbGluZyBmdW5jdGlvbnMNCi0gVW5kZXJzdGFuZCBSIE1hcmtkb3duDQotIFRoaXMgbGFiIGd1aWRlIGZvbGxvd3MgY2xvc2VseSBhbmQgc3VwcGxlbWVudHMgdGhlIG1hdGVyaWFsIHByZXNlbnRlZCBpbiBDaGFwdGVycyAyLCA0LCA1LCA3LCBhbmQgMjEgaW4gdGhlIHRleHRib29rIFtSIGZvciBEYXRhIFNjaWVuY2UgKFJEUyldKGh0dHBzOi8vcjRkcy5oYWRsZXkubnovKS4NCg0KXA0KDQojIFdoYXQgaXMgUj8NCg0KUiBpcyBhIGZyZWUsIG9wZW4gc291cmNlIHN0YXRpc3RpY2FsIHByb2dyYW1taW5nIGxhbmd1YWdlLiBJdCBpcyB1c2VmdWwgZm9yIGRhdGEgY2xlYW5pbmcsIGFuYWx5c2lzLCBhbmQgdmlzdWFsaXphdGlvbi4gUiBpcyBhbiBpbnRlcnByZXRlZCBsYW5ndWFnZSwgbm90IGEgY29tcGlsZWQgb25lLiBUaGlzIG1lYW5zIHRoYXQgeW91IHR5cGUgc29tZXRoaW5nIGludG8gUiBhbmQgaXQgZG9lcyB3aGF0IHlvdSB0ZWxsIGl0LiBJdCBpcyBib3RoIGEgY29tbWFuZCBsaW5lIHNvZnR3YXJlIGFuZCBhIHByb2dyYW1taW5nIGVudmlyb25tZW50LiBJdCBpcyBhbiBleHRlbnNpYmxlLCBvcGVuLXNvdXJjZSBsYW5ndWFnZSBhbmQgY29tcHV0aW5nIGVudmlyb25tZW50IGZvciBXaW5kb3dzLCBNYWNpbnRvc2gsIFVOSVgsIGFuZCBMaW51eCBwbGF0Zm9ybXMsIHdoaWNoIGFsbG93cyBmb3IgdGhlIHVzZXIgdG8gZnJlZWx5IGRpc3RyaWJ1dGUsIHN0dWR5LCBjaGFuZ2UsIGFuZCBpbXByb3ZlIHRoZSBzb2Z0d2FyZS4gSXQgaXMgYmFzaWNhbGx5IGEgZnJlZSwgc3VwZXIgYmlnLCBhbmQgY29tcGxleCBjYWxjdWxhdG9yLiBZb3Ugd2lsbCBiZSB1c2luZyBSIHRvIGFjY29tcGxpc2ggYWxsIGRhdGEgYW5hbHlzaXMgdGFza3MgaW4gdGhpcyBjbGFzcy4gWW91IG1pZ2h0IGJlIHdvbmRlcmluZyDigJxXaHkgaW4gdGhlIHdvcmxkIGRvIHdlIG5lZWQgdG8ga25vdyBob3cgdG8gdXNlIGEgc3RhdGlzdGljYWwgc29mdHdhcmUgcHJvZ3JhbT/igJ0gSGVyZSBhcmUgdGhlIG1haW4gcmVhc29uczoNCg0KMS4gWW91IHdpbGwgYmUgbGVhcm5pbmcgYWJvdXQgbmV3IGNvbmNlcHRzIGluIGxlY3R1cmUgYW5kIHRoZSByZWFkaW5ncy4gQXBwbHlpbmcgdGhlc2UgY29uY2VwdHMgdXNpbmcgcmVhbCBkYXRhIGlzIGFuIGltcG9ydGFudCBmb3JtIG9mIGxlYXJuaW5nLiBBIHN0YXRpc3RpY2FsIHNvZnR3YXJlIHByb2dyYW0gaXMgdGhlIG1vc3QgZWZmaWNpZW50IChhbmQgaW4gbWFueSBjYXNlcyB0aGUgb25seSkgbWVhbnMgb2YgcnVubmluZyBkYXRhIGFuYWx5c2VzLCBub3QganVzdCBpbiB0aGUgY2xvaXN0ZXJlZCBzZXR0aW5nIG9mIGEgdW5pdmVyc2l0eSBjbGFzc3Jvb20sIGJ1dCBlc3BlY2lhbGx5IGluIHRoZSByZWFsIHdvcmxkLiBBcHBsaWVkIGRhdGEgYW5hbHlzaXMgd2lsbCBiZSB0aGUgd2F5IHdlIGJyaWRnZSBzdGF0aXN0aWNhbCB0aGVvcnkgdG8gdGhlIOKAnHJlYWwgd29ybGQu4oCdIEFuZCBSIGlzIHRoZSB2ZWhpY2xlIGZvciBhY2NvbXBsaXNoaW5nIHRoaXMuDQoNCjIuIEluIG9yZGVyIHRvIGRvIGFwcGxpZWQgZGF0YSBhbmFseXNpcyBvdXRzaWRlIG9mIHRoZSBjbGFzc3Jvb20sIHlvdSBuZWVkIHRvIGtub3cgaG93IHRvIHVzZSBhIHN0YXRpc3RpY2FsIHByb2dyYW0uIFRoZXJlIGlzIG5vIHdheSBhcm91bmQgaXQuIElmIHlvdSB3YW50IHRvIGNvbGxlY3QgZGF0YSBvbiBoZWFsdGgsIHlvdSBuZWVkIGEgcHJvZ3JhbSB0byBzdG9yZSBhbmQgYW5hbHl6ZSB0aGF0IGRhdGEuIA0KDQpcDQoNClRoZSBuZXh0IHF1ZXN0aW9uIHlvdSBtYXkgaGF2ZSBpcyDigJxJIGxvdmUgRXhjZWwgb3IgU0FTIG9yIFNQU1Mgb3IgU3RhdGEgW29yIGluc2VydCB5b3VyIGZhdm9yaXRlIHByb2dyYW1dLiBXaHkgY2Fu4oCZdCBJIHVzZSB0aGF0IGFuZCBmb3JnZXQgeW91ciBzdHVwaWQgUj/igJ0gSGVyZSBhcmUgc29tZSByZWFzb25zOg0KDQoxLiBSIGlzIGZyZWUuIE1vc3QgcHJvZ3JhbXMgYXJlIG5vdC4NCjIuIFIgaXMgb3BlbiBzb3VyY2UuIFdoaWNoIG1lYW5zIHRoZSBzb2Z0d2FyZSBpcyBjb21tdW5pdHkgc3VwcG9ydGVkLiBUaGlzIGFsbG93cyB5b3UgdG8gZ2V0IGhlbHAgbm90IGZyb20gc29tZSBiaWcgY29ycG9yYXRpb24gKGUuZy4gTWljcm9zb2Z0IHdpdGggRXhjZWwpLCBidXQgcGVvcGxlIGFsbCBhcm91bmQgdGhlIHdvcmxkIHdobyBhcmUgdXNpbmcgUi4gQW5kIFIgaGFzICoqYSBsb3QqKiBvZiB1c2Vycywgd2hpY2ggbWVhbnMgdGhhdCBpZiB5b3UgaGF2ZSBhIHByb2JsZW0sIGFuZCB5b3UgcG9zZSBpdCB0byB0aGUgdXNlciBjb21tdW5pdHksIHNvbWVvbmUgd2lsbCBoZWxwIHlvdS4NCjMuIFIgaXMgcG93ZXJmdWwgYW5kIGV4dGVuc2libGUgKG1lYW5pbmcgdGhhdCBwcm9jZWR1cmVzIGZvciBhbmFseXppbmcgZGF0YSB0aGF0IGRvbuKAmXQgY3VycmVudGx5IGV4aXN0IGNhbiBiZSByZWFkaWx5IGRldmVsb3BlZCk7DQo0LiBSIGhhcyB0aGUgY2FwYWJpbGl0eSBmb3IgbWFwcGluZyBkYXRhLCBhbiBhc3NldCBub3QgZ2VuZXJhbGx5IGF2YWlsYWJsZSBpbiBvdGhlciBzdGF0aXN0aWNhbCBzb2Z0d2FyZS4NCjUuIElmIGl0IGlzbuKAmXQgYWxyZWFkeSwgUiBpcyBiZWNvbWluZyB0aGUgZGUtZmFjdG8gZGF0YSBhbmFseXNpcyB0b29sIGluIG1hbnkgZmllbGRzLCBpbmNsdWRpbmcgZm9yIG1hbnkgQ0EgRFBIIHBvc2l0aW9ucy4NCg0KXA0KDQpSIGlzIGRpZmZlcmVudCBmcm9tIEV4Y2VsIGluIHRoYXQgaXQgaXMgZ2VuZXJhbGx5IG5vdCBhIHBvaW50LWFuZC1jbGljayBwcm9ncmFtLiBZb3Ugd2lsbCBiZSBwcmltYXJpbHkgd3JpdGluZyBjb2RlIHRvIGNsZWFuIGFuZCBhbmFseXplIGRhdGEuIFdoYXQgZG9lcyAqd3JpdGluZyogb3IgKnNvdXJjaW5nKiBjb2RlIG1lYW4/IEEgYmFzaWMgZXhhbXBsZSB3aWxsIGhlbHAgY2xhcmlmeS4gTGV04oCZcyBzYXkgeW91IGFyZSBnaXZlbiBhIGRhdGFzZXQgd2l0aCBjYW5jZXIgY2FzZXMgYWNyb3NzIENBLiBZb3UgaGF2ZSBhIHZhcmlhYmxlIGluIHRoZSBkYXRhc2V0IHJlcHJlc2VudGluZyBhZ2UgTGV04oCZcyBzYXkgdGhpcyB2YXJpYWJsZSBpcyBuYW1lZCAqKkFHRSoqLiBUbyBnZXQgdGhlIG1lYW4gYWdlIG9mIHRoZSBwZW9wbGUgaW4geW91ciBkYXRhc2V0LCB5b3Ugd291bGQgd3JpdGUgY29kZSB0aGF0IHdvdWxkIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpcw0KDQoNCmBgYHtyfQ0KIyBEb3dubG9hZCBDYW5jZXIgRGF0YXNldA0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3BqYW1lcy11Y2RhdmlzL1NQSDIxNS9yZWZzL2hlYWRzL21haW4vQ0FfQ2FuY2VyX0RhdGEucmRzIiwgImNhX2NhbmNlci5yZHMiLCBtb2RlID0gIndiIikNCg0KIyBSZWFkIGluIENhbmNlciBEYXRhc2V0DQpjYW5jZXIgPC0gcmVhZFJEUygiY2FfY2FuY2VyLnJkcyIpDQoNCiMgR2V0IG5hbWVzIG9mIGNvbHVtbnMgb3IgdmFyaWFibGVzDQpoZWFkKGNhbmNlcikNCg0KIyBHZXQgbWVhbiBvZiBBR0UgdmFyaWFibGUNCm1lYW4oY2FuY2VyJEFHRSkNCmBgYA0KDQpcDQoNClRoZSBjb21tYW5kIHRlbGxzIHRoZSBwcm9ncmFtIHRvIGdldCB0aGUgbWVhbiBvZiB0aGUgdmFyaWFibGUgKipBR0UqKi4gSWYgeW91IHdhbnRlZCB0aGUgc3VtLCB5b3Ugd3JpdGUgdGhlIGNvbW1hbmQgYHN1bShjYW5jZXIkQUdFKWAuDQoNCk5vdywgd2hlcmUgZG8geW91IHdyaXRlIHRoaXMgY29tbWFuZD8gWW91IHdyaXRlIGl0IGluIGEgc2NyaXB0LiBBIHNjcmlwdCBpcyBiYXNpY2FsbHkgYSB0ZXh0IGZpbGUuIFRoaW5rIG9mIHdyaXRpbmcgY29kZSBhcyBzb21ldGhpbmcgc2ltaWxhciB0byB3cml0aW5nIGFuIGVzc2F5IGluIGEgd29yZCBkb2N1bWVudC4gSW5zdGVhZCBvZiBzZW50ZW5jZXMgdG8gcHJvZHVjZSBhbiBlc3NheSwgaW4gYSBwcm9ncmFtbWluZyBzY3JpcHQgeW91IGFyZSB3cml0aW5nIGxpbmVzIG9mIGNvZGUgdG8gcnVuIGEgZGF0YSBhbmFseXNpcy4gV2XigJlsbCBnbyB0aHJvdWdoIHNjcmlwdGluZyBpbiBtb3JlIGRldGFpbCBsYXRlciBpbiB0aGlzIGxhYiwgYnV0IHRoZSBiYXNpYyBwcm9jZXNzIG9mIHNvdXJjaW5nIGNvZGUgdG8gcnVuIGEgZGF0YSBhbmFseXNpcyB0YXNrIGlzIGFzIGZvbGxvd3MuDQoNCjEuIFdyaXRlIGNvZGUuIEZpcnN0LCB5b3Ugb3BlbiB5b3VyIHNjcmlwdCBmaWxlLCBhbmQgd3JpdGUgY29kZSBvciB2YXJpb3VzIGNvbW1hbmRzIChsaWtlIGBtZWFuKGNhbmNlciRBR0UpYCkgdGhhdCB3aWxsIGV4ZWN1dGUgZGF0YSBhbmFseXNpcyB0YXNrcyBpbiB0aGlzIGZpbGUuDQoyLiBTZW5kIGNvZGUgdG8gdGhlIHNvZnR3YXJlIHByb2dyYW0gdG8gcnVuIChSIGluIG91ciBjYXNlKS4NCjMuIFByb2dyYW0gcHJvZHVjZXMgcmVzdWx0cyBiYXNlZCBvbiBjb2RlLiBUaGUgcHJvZ3JhbSByZWFkcyBpbiB5b3VyIGNvbW1hbmRzIGZyb20gdGhlIHNjcmlwdCBhbmQgZXhlY3V0ZXMgdGhlbSwgc3BpdHRpbmcgb3V0IHJlc3VsdHMgaW4gaXRzIGNvbnNvbGUgc2NyZWVuLg0KDQpcDQoNCkkgYW0gc2tpcHBpbmcgb3ZlciBtYW55IGRldGFpbHMsIGJ1dCB0aGUgYWJvdmUgc3RlcHMgb3V0bGluZSB0aGUgZ2VuZXJhbCB3b3JrIGZsb3cuIFlvdSBtaWdodCBub3cgYmUgdGhpbmtpbmcgdGhhdCB5b3XigJlyZSBwZXJmZWN0bHkgaGFwcHkgcG9pbnRpbmcgYW5kIGNsaWNraW5nIHlvdXIgbW91c2UgaW4gRXhjZWwgKG9yIHdoZXJldmVyKSB0byBkbyB5b3VyIGRhdGEgYW5hbHlzaXMgdGFza3MuIFNvLCB3aHkgc2hvdWxkIHlvdSBhZG9wdCB0aGUgc3RhdGlzdGljYWwgcHJvZ3JhbW1pbmcgYXBwcm9hY2ggdG8gY29uZHVjdGluZyBhIGRhdGEgYW5hbHlzaXM/DQoNCjEuIFlvdXIgc2NyaXB0IGRvY3VtZW50cyB0aGUgZGVjaXNpb25zIHlvdSBtYWRlIGR1cmluZyB0aGUgZGF0YSBhbmFseXNpcyBwcm9jZXNzLiBUaGlzIGlzIGJlbmVmaWNpYWwgZm9yIG1hbnkgcmVhc29ucy4NCiAgLSBJdCBhbGxvd3MgeW91IHRvIHJlY3JlYXRlIHlvdXIgc3RlcHMgaWYgeW91IG5lZWQgdG8gcmVydW4gb3IgYWx0ZXIgeW91ciBhbmFseXNpcyBtYW55IHdlZWtzLCBtb250aHMsIG9yIGV2ZW4geWVhcnMgaW4gdGhlIGZ1dHVyZS4NCiAgLSBJdCBhbGxvd3MgeW91IHRvIHNoYXJlIHlvdXIgc3RlcHMgd2l0aCBvdGhlciBwZW9wbGUuIElmIHNvbWVvbmUgYXNrcyB5b3Ugd2hhdCB3ZXJlIHRoZSBkZWNpc2lvbnMgbWFkZSBpbiB0aGUgZGF0YSBhbmFseXNpcyBwcm9jZXNzLCAtIGp1c3QgaGFuZCB0aGVtIHRoZSBzY3JpcHQuDQogIC0gUmVsYXRlZCB0byB0aGUgYWJvdmUgcG9pbnRzLCBhIHNjcmlwdCBwcm9tb3RlcyAqKnRyYW5zcGFyZW5jeSoqIChoZXJlIGlzIHdoYXQgSSBkaWQpIGFuZCAqKnJlcHJvZHVjaWJpbGl0eSoqICh5b3UgY2FuIGRvIGl0IHRvbykuIFdoZW4geW91IHdyaXRlIGNvZGUsIHlvdSBhcmUgZm9yY2VkIHRvIGV4cGxpY2l0bHkgc3RhdGUgdGhlIHN0ZXBzIHlvdSB0b29rIHRvIGRvIHlvdXIgcmVzZWFyY2guIFdoZW4geW91IGRvIHJlc2VhcmNoIGJ5IGNsaWNraW5nIHRocm91Z2ggZHJvcC1kb3duIG1lbnVzLCB5b3VyIHN0ZXBzIGFyZSBsb3N0LCBvciBhdCBsZWFzdCBkb2N1bWVudGluZyB0aGVtIHJlcXVpcmVzIGNvbnNpZGVyYWJsZSBleHRyYSBlZmZvcnQuDQogIA0KMi4gSWYgeW91IG1ha2UgYSBtaXN0YWtlIGluIGEgZGF0YSBhbmFseXNpcyBzdGVwLCB5b3UgY2FuIGdvIGJhY2ssIGNoYW5nZSBhIGZldyBsaW5lcyBvZiBjb2RlLCBhbmQgKipwb29mKiosIHlvdeKAmXZlIGZpeGVkIHlvdXIgcHJvYmxlbS4NCg0KMy4gSXQgaXMgbW9yZSBlZmZpY2llbnQuIEluIHBhcnRpY3VsYXIsIGNsZWFuaW5nIGRhdGEgY2FuIGVuY29tcGFzcyBhIGxvdCBvZiB0ZWRpb3VzIHdvcmsgdGhhdCBjYW4gYmUgc3RyZWFtbGluZWQgdXNpbmcgc3RhdGlzdGljYWwgcHJvZ3JhbW1pbmcuDQoNCkhvcGVmdWxseSwgSeKAmXZlIGNvbnZpbmNlZCB5b3UgdGhhdCBzdGF0aXN0aWNhbCBwcm9ncmFtbWluZyBhbmQgUiBhcmUgd29ydGh3aGlsZSB0byBsZWFybi4gTm93IGxldCdzIHRhbGsgYWJvdXQgZ2V0dGluZyBSIG9uIHlvdXIgY29tcHV0ZXIhDQoNClwNCg0KIyBHZXR0aW5nIFINCg0KUiBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIG9uZSBvZiB0aGUg4oCcQ1JBTuKAnSAoQ29tcHJlaGVuc2l2ZSBSIEFyY2hpdmUgTmV0d29yaykgc2l0ZXMuIEluIHRoZSBVUywgdGhlIG1haW4gc2l0ZSBpcyBhdCBodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnLy4gTG9vayBpbiB0aGUg4oCcRG93bmxvYWQgYW5kIEluc3RhbGwgUuKAnSBhcmVhIGF0IHRoZSB0b3AuIENsaWNrIG9uIHRoZSBhcHByb3ByaWF0ZSBsaW5rIGJhc2VkIG9uIHlvdXIgb3BlcmF0aW5nIHN5c3RlbS4NCg0KKipJZiB5b3UgYWxyZWFkeSBoYXZlIFIgb24geW91ciBjb21wdXRlciwgbWFrZSBzdXJlIHlvdSBoYXZlIHRoZSBtb3N0IHVwZGF0ZWQgdmVyc2lvbiBvZiBSIG9uIHlvdXIgcGVyc29uYWwgY29tcHV0ZXIgKFIgdmVyc2lvbiA0LjUuMiAoW05vdF0gUGFydCBpbiBhIFJ1bWJsZSkpLioqDQoNClwNCg0KIyMgTWFjIE9TIFgNCjEuIE9uIHRoZSDigJxSIGZvciBNYWMgT1PigJ0gcGFnZSwgdGhlcmUgYXJlIG11bHRpcGxlIHBhY2thZ2VzIHRoYXQgY291bGQgYmUgZG93bmxvYWRlZC4gRGVwZW5kaW5nIG9uIHRoZSBtb2RlbCBvZiB5b3VyIE1hYywgcGljayB0aGUgYXBwcm9wcmlhdGUgLnBrZyBmaWxlLiAqTm90ZSB0aGUgZGV0YWlscyBmb3Igc29tZSBvcGVyYXRpbmcgc3lzdGVtcy4gSWYgeW91IGFyZSB1c2luZyBhbiBvbGRlciBvcGVyYXRpbmcgc3lzdGVtLCBwbGVhc2UgZm9sbG93IGluc3RydWN0aW9ucy4qDQoNCjIuIEFmdGVyIHRoZSBwYWNrYWdlIGZpbmlzaGVzIGRvd25sb2FkaW5nLCBsb2NhdGUgdGhlIGluc3RhbGxlciBvbiB5b3VyIGhhcmQgZHJpdmUsIGRvdWJsZS1jbGljayBvbiB0aGUgaW5zdGFsbGVyIHBhY2thZ2UsIGFuZCBhZnRlciBhIGZldyBzY3JlZW5zLCBzZWxlY3QgYSBkZXN0aW5hdGlvbiBmb3IgdGhlIGluc3RhbGxhdGlvbiBvZiB0aGUgUiBmcmFtZXdvcmsgKHRoZSBwcm9ncmFtKSBhbmQgdGhlIFIuYXBwIEdVSS4gTm90ZSB0aGF0IHlvdSB3aWxsIGhhdmUgdG8gc3VwcGx5IHRoZSBBZG1pbmlzdHJhdG9y4oCZcyBwYXNzd29yZC4gQ2xvc2UgdGhlIHdpbmRvdyB3aGVuIHRoZSBpbnN0YWxsYXRpb24gaXMgZG9uZS4NCg0KMy4gQW4gYXBwbGljYXRpb24gd2lsbCBhcHBlYXIgaW4gdGhlIEFwcGxpY2F0aW9ucyBmb2xkZXI6IFIuYXBwLg0KDQo0LiBCcm93c2UgdG8gdGhlIFtYUXVhcnR6XShodHRwczovL3d3dy54cXVhcnR6Lm9yZy8pIGRvd25sb2FkIHBhZ2UuIENsaWNrIG9uIHRoZSBtb3N0IHJlY2VudCB2ZXJzaW9uIG9mIFhRdWFydHogdG8gZG93bmxvYWQgdGhlIGFwcGxpY2F0aW9uLg0KIA0KNS4gUnVuIHRoZSBYUXVhcnR6IGluc3RhbGxlci4gWFF1YXJ0eiBpcyBuZWVkZWQgdG8gY3JlYXRlIHdpbmRvd3MgdG8gZGlzcGxheSBtYW55IHR5cGVzIG9mIFIgZ3JhcGhpY3M6IHRoaXMgdXNlZCB0byBiZSBpbmNsdWRlZCBpbiBNYWNPUyB1bnRpbCB2ZXJzaW9uIDEwLjggYnV0IG5vdyBtdXN0IGJlIGRvd25sb2FkZWQgc2VwYXJhdGVseS4NCg0KXA0KDQojIyBXaW5kb3dzDQoxLiBPbiB0aGUg4oCcUiBmb3IgV2luZG93c+KAnSBwYWdlLCBjbGljayBvbiB0aGUg4oCcYmFzZeKAnSBsaW5rLCB3aGljaCBzaG91bGQgdGFrZSB5b3UgdG8gdGhlIOKAnFItNC41LjIgZm9yIFdpbmRvd3PigJ0gcGFnZQ0KDQoyLiBPbiB0aGlzIHBhZ2UsIGNsaWNrIOKAnERvd25sb2FkIFItNC41LjIgZm9yIFdpbmRvd3PigJ0sIGFuZCBzYXZlIHRoZSAuZXhlIGZpbGUgdG8geW91ciBoYXJkIGRpc2sgd2hlbiBwcm9tcHRlZC4gU2F2aW5nIHRvIHRoZSBkZXNrdG9wIGlzIGZpbmUuDQoNCjMuIFRvIGJlZ2luIHRoZSBpbnN0YWxsYXRpb24sIGRvdWJsZS1jbGljayBvbiB0aGUgZG93bmxvYWRlZCBmaWxlLiBEb27igJl0IGJlIGFsYXJtZWQgaWYgeW91IGdldCB1bmtub3duIHB1Ymxpc2hlciB0eXBlIHdhcm5pbmdzLiBXaW5kb3figJlzIFVzZXIgQWNjb3VudCBDb250cm9sIHdpbGwgYWxzbyB3b3JyeSBhYm91dCBhbiB1bmlkZW50aWZpZWQgcHJvZ3JhbSB3YW50aW5nIGFjY2VzcyB0byB5b3VyIGNvbXB1dGVyLiBDbGljayBvbiDigJxSdW7igJ0uDQoNCjQuIFNlbGVjdCB0aGUgcHJvcG9zZWQgb3B0aW9ucyBpbiBlYWNoIHBhcnQgb2YgdGhlIGluc3RhbGwgZGlhbG9nLiBXaGVuIHRoZSDigJxTZWxlY3QgQ29tcG9uZW50c+KAnSBzY3JlZW4gYXBwZWFycywganVzdCBhY2NlcHQgdGhlIHN0YW5kYXJkIGNob2ljZXMNCg0KXA0KDQojIFdoYXQgaXMgUiBTdHVkaW8/DQpJZiB5b3UgY2xpY2sgb24gdGhlIFIgcHJvZ3JhbSB5b3UganVzdCBkb3dubG9hZGVkLCB5b3Ugd2lsbCBmaW5kIGEgdmVyeSBiYXNpYyB1c2VyIGludGVyZmFjZS4gRm9yIGV4YW1wbGUsIGJlbG93IGlzIHdoYXQgSSBnZXQgb24gYSBNYWM6DQoNCiFbVGhlIEJhc2ljIFIgQ29uc29sZV0oUl9iYXNpYy5wbmcpDQoNClwNCg0KV2Ugd2lsbCBub3QgdXNlIFLigJlzIGRpcmVjdCBpbnRlcmZhY2UgdG8gcnVuIGFuYWx5c2VzIGluIHRoaXMgY2xhc3MuIEluc3RlYWQsIHdlIHdpbGwgdXNlIHRoZSBwcm9ncmFtICoqUlN0dWRpbyoqLCB3aGljaCBpcyBtdWNoIGVhc2llciB0byBpbnRlcmFjdCB3aXRoISBSU3R1ZGlvIGdpdmVzIHlvdSBhIHRydWUgaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudCAoSURFKSwgd2hlcmUgeW91IGNhbiB3cml0ZSBjb2RlIGluIGEgd2luZG93LCBzZWUgcmVzdWx0cyBpbiBvdGhlciB3aW5kb3dzLCBzZWUgbG9jYXRpb25zIG9mIGZpbGVzLCBzZWUgb2JqZWN0cyB5b3XigJl2ZSBjcmVhdGVkLCBhbmQgc28gb24uIFRvIGNsYXJpZnkgd2hpY2ggaXMgd2hpY2g6IFIgaXMgdGhlIG5hbWUgb2YgdGhlIHByb2dyYW1taW5nIGxhbmd1YWdlIGl0c2VsZiBhbmQgUlN0dWRpbyBpcyBhbiBpbnRlcmZhY2UgdGhhdCBtYWtlcyB3cml0aW5nIGNvZGUsIHJ1bm5pbmcgYW5hbHlzZXMsIGFuZCB2aXN1YWxpemluZyBkYXRhIGluIFIgc28gbXVjaCBlYXNpZXIuDQoNClwNCg0KIyBHZXR0aW5nIFIgU3R1ZGlvDQoNClRvIGRvd25sb2FkIGFuZCBpbnN0YWxsIFJTdHVkaW8sIGZvbGxvdyB0aGUgZGlyZWN0aW9ucyBiZWxvdw0KDQoxLiBOYXZpZ2F0ZSB0byBbUlN0dWRpb+KAmXMgZG93bmxvYWQgc2l0ZV0oaHR0cHM6Ly9wb3NpdC5jby9kb3dubG9hZC9yc3R1ZGlvLWRlc2t0b3AvKQ0KDQoyLiBXZSd2ZSBhbHJlYWR5IGRvd25sb2FkZWQgUiwgc28gY2xpY2sgb24gdGhlIGFwcHJvcHJpYXRlIGxpbmsgdG8gSW5zdGFsbCBSU3R1ZGlvIGJhc2VkIG9uIHlvdXIgT1MgKFdpbmRvd3MsIE1hYywgTGludXggYW5kIG1hbnkgb3RoZXJzKS4gRG8gbm90IGRvd25sb2FkIGFueXRoaW5nIGZyb20gdGhlIOKAnEFsbCBJbnN0YWxsZXJzIGFuZCBUYXJiYWxsc+KAnSBzZWN0aW9uLg0KDQozLiBDbGljayBvbiB0aGUgaW5zdGFsbGVyIHRoYXQgeW91IGRvd25sb2FkZWQuIEZvbGxvdyB0aGUgaW5zdGFsbGF0aW9uIGRpcmVjdGlvbnMsIG1ha2luZyBzdXJlIHRvIGtlZXAgYWxsIGRlZmF1bHRzIGludGFjdC4gQWZ0ZXIgaW5zdGFsbGF0aW9uLCBSU3R1ZGlvIHNob3VsZCBwb3AgdXAgaW4geW91ciBBcHBsaWNhdGlvbnMgb3IgUHJvZ3JhbXMgZm9sZGVyL21lbnUuDQoNClwNCg0KIyMgVGhlIFJTdHVkaW8gSW50ZXJmYWNlDQoNCk9wZW4gdXAgUlN0dWRpby4gWW91IHNob3VsZCBzZWUgdGhlIGludGVyZmFjZSBzaG93biBpbiB0aGUgZmlndXJlIGJlbG93IHdoaWNoIGhhcyB0aHJlZSB3aW5kb3dzLg0KDQohW1RoZSBSU3R1ZGlvIENvbnNvbGVdKFJTdHVkaW9faW1hZ2UucG5nKQ0KDQpcDQoNCi0gKipDb25zb2xlKiogKGJvdHRvbSBsZWZ0KSAtIFRoZSB3YXkgUiB3b3JrcyBpcyB5b3Ugd3JpdGUgYSBsaW5lIG9mIGNvZGUgdG8gZXhlY3V0ZSBzb21lIGtpbmQgb2YgdGFzayBvbiBhIGRhdGEgb2JqZWN0LiAtIFRoZSBSIENvbnNvbGUgYWxsb3dzIHlvdSB0byBydW4gY29kZSBpbnRlcmFjdGl2ZWx5LiBUaGUgc2NyZWVuIHByb21wdCBgPmAgaXMgYW4gaW52aXRhdGlvbiBmcm9tIFIgdG8gZW50ZXIgaXRzIC0gd29ybGQuIFRoaXMgaXMgd2hlcmUgeW91IHR5cGUgY29kZSBpbiwgcHJlc3MgZW50ZXIgdG8gZXhlY3V0ZSB0aGUgY29kZSwgYW5kIHNlZSB0aGUgcmVzdWx0cy4NCi0gKipFbnZpcm9ubWVudCwgSGlzdG9yeSwgYW5kIENvbm5lY3Rpb25zIHRhYnMqKiAodXBwZXItcmlnaHQpDQogIC0gKipFbnZpcm9ubWVudCoqIC0gc2hvd3MgYWxsIHRoZSBSIG9iamVjdHMgdGhhdCBhcmUgY3VycmVudGx5IG9wZW4gaW4geW91ciB3b3Jrc3BhY2UuIFRoaXMgaXMgdGhlIHBsYWNlLCBmb3IgZXhhbXBsZSwgd2hlcmUgeW91IHdpbGwgc2VlIGFueSBkYXRhIHlvdeKAmXZlIGxvYWRlZCBpbnRvIFIuIFdoZW4geW91IGV4aXQgUlN0dWRpbywgUiB3aWxsIGNsZWFyIGFsbCBvYmplY3RzIGluIHRoaXMgd2luZG93LiBZb3UgY2FuIGFsc28gY2xpY2sgb24gIVticm9vbV0oYnJvb20ucG5nKSB0byBjbGVhciBvdXQgYWxsIHRoZSBvYmplY3RzIGxvYWRlZCBhbmQgY3JlYXRlZCBpbiB5b3VyIGN1cnJlbnQgc2Vzc2lvbi4NCiAgLSAqKkhpc3RvcnkqKiAtIHNob3dzIGEgbGlzdCBvZiBleGVjdXRlZCBjb21tYW5kcyBpbiB0aGUgY3VycmVudCBzZXNzaW9uLg0KICAtICoqQ29ubmVjdGlvbnMqKiAtIHlvdSBjYW4gY29ubmVjdCB0byBhIHZhcmlldHkgb2YgZGF0YSBzb3VyY2VzLCBhbmQgZXhwbG9yZSB0aGUgb2JqZWN0cyBhbmQgZGF0YSBpbnNpZGUgdGhlIGNvbm5lY3Rpb24uIEkgdHlwaWNhbGx5IGRvbuKAmXQgdXNlIHRoaXMgd2luZG93LCBidXQgeW91IFtjYW5dKGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8xMTUwMTA5MTU2ODctVXNpbmctUlN0dWRpby1Db25uZWN0aW9ucykuDQotICoqRmlsZXMsIFBsb3RzLCBQYWNrYWdlcywgSGVscCBhbmQgVmlld2VyIHRhYnMqKiAobG93ZXItcmlnaHQpDQogIC0gKipGaWxlcyoqIOKAkyBzaG93cyBhbGwgdGhlIGZpbGVzIGFuZCBmb2xkZXJzIGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeQ0KICAtICoqUGxvdHMqKiDigJMgc2hvd3MgYW55IGNoYXJ0cywgZ3JhcGhzLCBtYXBzIGFuZCBwbG90cyB5b3XigJl2ZSBleGVjdXRlZA0KICAtICoqUGFja2FnZXMqKiDigJMgc2hvd3MgYXZhaWxhYmxlIFIgcGFja2FnZXMNCiAgLSAqKkhlbHAqKiDigJMgZGlzcGxheXMgaGVscCBkb2N1bWVudGF0aW9uDQogIC0gKipWaWV3ZXIqKiDigJMgZGlzcGxheXMgbG9jYWwgd2ViIGNvbnRlbnQNCg0KDQpUaGVyZSBpcyBhbHNvIGEgZm91cnRoIHdpbmRvdy4gQnV0LCB3ZeKAmWxsIGdldCB0byB0aGlzIHdpbmRvdyBhIGxpdHRsZSBsYXRlci4gVGhlIFthc3NpZ25tZW50IGd1aWRlbGluZXNdKEFzc2lnbm1lbnRzXzIwMjYuaHRtbCkgYWxzbyBoYXZlIG1vcmUgb24gdGhpcyB3aW5kb3chDQoNClwNCg0KIyMgU2V0dGluZyBSU3R1ZGlvIERlZmF1bHRzDQoNCldoaWxlIG5vdCByZXF1aXJlZCwgSSBzdHJvbmdseSBzdWdnZXN0IHRoYXQgeW91IGNoYW5nZSBwcmVmZXJlbmNlcyBpbiBSU3R1ZGlvIHRvIG5ldmVyIHNhdmUgdGhlIHdvcmtzcGFjZSBzbyB5b3UgYWx3YXlzIG9wZW4gd2l0aCBhIGNsZWFuIGVudmlyb25tZW50LiBTZWUgW0NoLiA4LjFdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovd29ya2Zsb3ctcHJvamVjdHMuaHRtbCN3aGF0LWlzLXJlYWwpIG9mIFI0RFMgZm9yIHNvbWUgbW9yZSBiYWNrZ3JvdW5kDQoNCjEuIEZyb20gdGhlIFRvb2xzIG1lbnUgb24gUlN0dWRpbywgb3BlbiB0aGUgVG9vbHMgbWVudSBhbmQgdGhlbiBzZWxlY3QgR2xvYmFsIE9wdGlvbnMuDQoNCjIuIElmIG5vdCBhbHJlYWR5IGhpZ2hsaWdodGVkLCBjbGljayBvbiB0aGUgR2VuZXJhbCBidXR0b24gZnJvbSB0aGUgbGVmdCBwYW5lbC4NCg0KMy4gVW5jaGVjayB0aGUgZm9sbG93aW5nIFJlc3RvcmUgYm94ZXMNCiAgLSBSZXN0b3JlIG1vc3QgcmVjZW50bHkgb3BlbmVkIHByb2plY3QgYXQgc3RhcnR1cA0KICAtIFJlc3RvcmUgcHJldmlvdXNseSBvcGVuIHNvdXJjZSBkb2N1bWVudHMgYXQgc3RhcnR1cA0KICAtIFJlc3RvcmUgLlJEYXRhIGludG8gd29ya3NwYWNlIGF0IHN0YXJ0dXANCg0KNC4gU2V0IFNhdmUgV29ya3NwYWNlIHRvIC5SRGF0YSBvbiBleGl0IHRvICJOZXZlciIuDQoNCjUuIENsaWNrIE9LIGF0IHRoZSBib3R0b20gdG8gc2F2ZSB0aGUgY2hhbmdlcyBhbmQgY2xvc2UgdGhlIHByZWZlcmVuY2VzIHdpbmRvdy4gWW91IG1heSBuZWVkIHRvIHJlc3RhcnQgUlN0dWRpby4NCg0KVGhlIHJlYXNvbiBmb3IgbWFraW5nIHRoZXNlIGNoYW5nZXMgaXMgdGhhdCBpdCBpcyBwcmVmZXJhYmxlIGZvciByZXByb2R1Y2liaWxpdHkgdG8gc3RhcnQgZWFjaCBSIHNlc3Npb24gd2l0aCBhIGNsZWFuIGVudmlyb25tZW50LiBZb3UgY2FuIHJlc3RvcmUgYSBwcmV2aW91cyBlbnZpcm9ubWVudCBlaXRoZXIgYnkgcmVydW5uaW5nIGNvZGUgb3IgYnkgbWFudWFsbHkgbG9hZGluZyBhIHByZXZpb3VzbHkgc2F2ZWQgc2Vzc2lvbi4NCg0KVGhlIFIgU3R1ZGlvIGVudmlyb25tZW50IGlzIG1vZGlmaWVkIHdoZW4geW91IGV4ZWN1dGUgY29kZSBmcm9tIGZpbGVzIG9yIGZyb20gdGhlIGNvbnNvbGUuIElmIHlvdSBhbHdheXMgc3RhcnQgZnJlc2gsIHlvdSBkbyBub3QgbmVlZCB0byBiZSBjb25jZXJuZWQgYWJvdXQgdGhpbmdzIG5vdCB3b3JraW5nIGJlY2F1c2Ugb2Ygc29tZXRoaW5nIHlvdSB0eXBlZCBpbiB0aGUgY29uc29sZSwgYnV0IGRpZCBub3Qgc2F2ZSBpbiBhIGZpbGUuDQoNCllvdSBvbmx5IG5lZWQgdG8gc2V0IHRoZXNlIHByZWZlcmVuY2VzIG9uY2UuDQoNClwNCg0KIyBSIERhdGEgVHlwZXMNCg0KTGV04oCZcyBub3cgZXhwbG9yZSB3aGF0IFIgY2FuIGRvLiBSIGlzIHJlYWxseSBqdXN0IGEgYmlnIGZhbmN5IGNhbGN1bGF0b3IuIEZvciBleGFtcGxlLCB0eXBlIGluIHRoZSBmb2xsb3dpbmcgbWF0aGVtYXRpY2FsIGV4cHJlc3Npb24gaW4gdGhlIFIgY29uc29sZSAobGVmdCB3aW5kb3cpOg0KYGBge3IgcjF9DQoxKzENCmBgYA0KTm90ZSB0aGF0IHNwYWNpbmcgZG9lcyBub3QgbWF0dGVyOiBgMSsxYCB3aWxsIGdlbmVyYXRlIHRoZSBzYW1lIGFuc3dlciBhcyBgMSAgICAgICsgICAgICAgMWAuIENhbiB5b3Ugc2F5IGhlbGxvIHRvIHRoZSB3b3JsZD8NCmBgYHtyIHIyfQ0KImhlbGxvIHdvcmxkIg0KYGBgDQpMb29rcyBncmVhdCEgKipOb3RlLCB3ZSBuZWVkIHRvIHB1dCBxdW90ZXMgYXJvdW5kIGl0LioqIOKAnGhlbGxvIHdvcmxk4oCdIGlzIGEgY2hhcmFjdGVyIGFuZCBSIHJlY29nbml6ZXMgY2hhcmFjdGVycyBvbmx5IGlmIHRoZXJlIGFyZSBxdW90ZXMgYXJvdW5kIGl0LiBUaGlzIGJyaW5ncyB1cyB0byB0aGUgdG9waWMgb2YgYmFzaWMgZGF0YSB0eXBlcyBpbiBSLiBUaGVyZSBhcmUgZm91ciBiYXNpYyBkYXRhIHR5cGVzIGluIFI6IGNoYXJhY3RlciwgbG9naWNhbCwgbnVtZXJpYywgYW5kIGZhY3RvcnMgKHRoZXJlIGFyZSB0d28gb3RoZXJzIC0gY29tcGxleCBhbmQgcmF3IC0gYnV0IHdlIHdvbuKAmXQgY292ZXIgdGhlbSBiZWNhdXNlIHRoZXkgYXJlIHJhcmVseSB1c2VkIGluIHByYWN0aWNlKS4NCg0KXA0KDQojIyBDaGFyYWN0ZXJzDQpDaGFyYWN0ZXJzIGFyZSB1c2VkIHRvIHJlcHJlc2VudCB3b3JkcyBvciBsZXR0ZXJzIGluIFIuIFdlIHNhdyB0aGlzIGFib3ZlIHdpdGgg4oCcaGVsbG8gd29ybGTigJ0uIENoYXJhY3RlciB2YWx1ZXMgYXJlIGFsc28ga25vd24gYXMgc3RyaW5ncy4gWW91IG1pZ2h0IHRoaW5rIHRoYXQgdGhlIHZhbHVlIGAiMSJgIGlzIGEgbnVtYmVyLiBXZWxsLCBpZiB5b3UgcHV0IHF1b3RlcyBhcm91bmQsIGl0IGlzbuKAmXQhIEFueXRoaW5nIHdpdGggcXVvdGVzIHdpbGwgYmUgaW50ZXJwcmV0ZWQgYXMgYSBjaGFyYWN0ZXIuIE5vIGlmcywgYW5kcyBvciBidXRzIGFib3V0IGl0Lg0KDQpcDQoNCiMjIExvZ2ljYWxzDQoNCkEgbG9naWNhbCB0YWtlcyBvbiB0d28gdmFsdWVzOiBgRkFMU0VgIG9yIGBUUlVFYC4gTG9naWNhbHMgYXJlIHVzdWFsbHkgY29uc3RydWN0ZWQgd2l0aCBjb21wYXJpc29uIG9wZXJhdG9ycywgd2hpY2ggd2XigJlsbCBnbyB0aHJvdWdoIG1vcmUgY2FyZWZ1bGx5IGluIExhYiAyLiBUaGluayBvZiBhIGxvZ2ljYWwgYXMgdGhlIGFuc3dlciB0byBhIHF1ZXN0aW9uIGxpa2Ug4oCcSXMgdGhpcyB2YWx1ZSBncmVhdGVyIHRoYW4gKGxvd2VyIHRoYW4vZXF1YWwgdG8pIHRoaXMgb3RoZXIgdmFsdWU/4oCdIFRoZSBhbnN3ZXIgd2lsbCBiZSBlaXRoZXIgYFRSVUVgIG9yIGBGQUxTRWAuIGBUUlVFYCBhbmQgYEZBTFNFYCBhcmUgbG9naWNhbCB2YWx1ZXMgaW4gUi4gRm9yIGV4YW1wbGUsIHR5cGluZyBpbiB0aGUgZm9sbG93aW5nDQoNCmBgYHtyIHIzfQ0KMyA+IDINCmBgYA0KDQpcDQoNClRoaXMgZ2l2ZXMgeW91IGEgYFRSVUVgIFdoYXQgYWJvdXQgdGhlIGZvbGxvd2luZz8NCmBgYHtyIHI0fQ0KImRlY2xhbiIgPT0gImNhdGhlcmluZSINCmBgYA0KDQpcDQoNCiMjIE51bWVyaWMNCk51bWVyaWNzIGFyZSBzZXBhcmF0ZWQgaW50byB0d28gdHlwZXM6IGludGVnZXIgYW5kIGRvdWJsZS4gVGhlIGRpc3RpbmN0aW9uIGJldHdlZW4gaW50ZWdlcnMgYW5kIGRvdWJsZXMgaXMgdXN1YWxseSBub3QgaW1wb3J0YW50LiBSIHRyZWF0cyBudW1lcmljcyBhcyBkb3VibGVzIGJ5IGRlZmF1bHQgYmVjYXVzZSBpdCBpcyBhIGxlc3MgcmVzdHJpY3RpdmUgZGF0YSB0eXBlLiBZb3UgY2FuIGRvIGFueSBtYXRoZW1hdGljYWwgb3BlcmF0aW9uIG9uIG51bWVyaWMgdmFsdWVzLiBXZSBhZGRlZCBvbmUgYW5kIG9uZSBhYm92ZS4gV2UgY2FuIGFsc28gbXVsdGlwbHkgdXNpbmcgdGhlIGAqYCBvcGVyYXRvci4NCg0KYGBge3IgcjV9DQoyKjMNCmBgYA0KDQpcDQoNCkFuZCBkaXZpZGUNCmBgYHtyIHI2fQ0KMi8zDQpgYGANCg0KXA0KDQpBbmQgdGFrZSBsb2dzDQpgYGB7cn0NCmxvZygxKQ0KYGBgDQoNClwNCg0KYGBge3J9DQpsb2coMCkNCmBgYA0KDQpIb2xkIHVwISBXaGF0IGlzIGAtSW5mYD8gV2VsbCwgeW91IGNhbuKAmXQgdGFrZSB0aGUgbG9nYXJpdGhtIG9mIDAsIHNvIFIgaXMgdGVsbGluZyB5b3UgdGhhdCB5b3XigJlyZSBnZXR0aW5nIGEgbm9uIG51bWVyaWMgdmFsdWUgaW4gcmV0dXJuLiBUaGUgdmFsdWUgYC1JbmZgIGlzIGFub3RoZXIgdmFsdWUgdHlwZSB0aGF0IHlvdSBjYW4gZ2V0IGluIFIuDQoNClwNCg0KIyMgRmFjdG9ycw0KDQpUaGluayBvZiBhIGZhY3RvciBhcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBJdCBpcyBzb3J0IG9mIGxpa2UgYSBjaGFyYWN0ZXIsIGJ1dCBub3QgcmVhbGx5LiBJdCBpcyBhY3R1YWxseSBhIG51bWVyaWMgY29kZSB3aXRoIGNoYXJhY3Rlci12YWx1ZWQgbGV2ZWxzLiBUaGluayBvZiBhIGNoYXJhY3RlciBhcyBhIHRydWUgc3RyaW5nIGFuZCBhIGZhY3RvciBhcyBhIHNldCBvZiBjYXRlZ29yaWVzIHJlcHJlc2VudGVkIGFzIGNoYXJhY3RlcnMuIFdlIHdvbuKAmXQgdXNlIGZhY3RvcnMgdG9vIG11Y2ggaW4gdGhpcyBjb3Vyc2UsIHNvIG1heWJlIGRvbid0IHdvcnJ5IGFib3V0IGl0IGZvciBub3chDQoNClwNCg0KIyBSIERhdGEgU3RydWN0dXJlcw0KWW91IGp1c3QgbGVhcm5lZCB0aGF0IFIgaGFzIGZvdXIgYmFzaWMgZGF0YSB0eXBlcy4gTm93LCBsZXTigJlzIGdvIHRocm91Z2ggaG93IHdlIGNhbiBzdG9yZSBkYXRhIGluIFIuIFRoYXQgaXMsIHlvdSB0eXBlIGluIHRoZSBjaGFyYWN0ZXIg4oCcaGVsbG8gd29ybGTigJ0gb3IgdGhlIG51bWJlciAzLCBhbmQgeW91IHdhbnQgdG8gc3RvcmUgdGhlc2UgdmFsdWVzLiBZb3UgZG8gdGhpcyBieSB1c2luZyBS4oCZcyB2YXJpb3VzIGRhdGEgc3RydWN0dXJlcy4NCg0KIyMgVmVjdG9ycw0KDQpBIHZlY3RvciBpcyB0aGUgbW9zdCBjb21tb24gYW5kIGJhc2ljIFIgZGF0YSBzdHJ1Y3R1cmUgYW5kIGlzIHByZXR0eSBtdWNoIHRoZSB3b3JraG9yc2Ugb2YgdGhlIGxhbmd1YWdlLiBBIHZlY3RvciBpcyBzaW1wbHkgYSBzZXF1ZW5jZSBvZiB2YWx1ZXMgd2hpY2ggY2FuIGJlIG9mIGFueSBkYXRhIHR5cGUgYnV0IGFsbCBvZiB0aGUgc2FtZSB0eXBlLiBUaGVyZSBhcmUgYSBudW1iZXIgb2Ygd2F5cyB0byBjcmVhdGUgYSB2ZWN0b3IgZGVwZW5kaW5nIG9uIHRoZSBkYXRhIHR5cGUsIGJ1dCB0aGUgbW9zdCBjb21tb24gaXMgdG8gaW5zZXJ0IHRoZSBkYXRhIHlvdSB3YW50IHRvIHNhdmUgaW4gYSB2ZWN0b3IgaW50byB0aGUgY29tbWFuZCBgYygpYC4gRm9yIGV4YW1wbGUsIHRvIHJlcHJlc2VudCB0aGUgdmFsdWVzIDQsIDE2LCBhbmQgOSBpbiBhIHZlY3RvciB0eXBlIGluDQoNCmBgYHtyfQ0KYyg0LCAxNiwgOSkNCmBgYA0KDQpcDQoNCllvdSBjYW4gYWxzbyBoYXZlIGEgdmVjdG9yIG9mIGNoYXJhY3RlciB2YWx1ZXMNCmBgYHtyfQ0KYygiY2F0aGVyaW5lIiwgImRlY2xhbiIsICJnd2VuIikNCmBgYA0KDQpUaGUgYWJvdmUgY29kZSBkb2VzIG5vdCBhY3R1YWxseSDigJxzYXZl4oCdIHRoZSB2YWx1ZXMgNCwgMTYsIGFuZCA5IG9yIGNhdGhlcmluZSwgZGVjbGFuLCBnd2VuIC0tIGl0IGp1c3QgcHJlc2VudHMgaXQgb24gdGhlIHNjcmVlbiBpbiBhIHZlY3Rvci4gSWYgeW91IHdhbnQgdG8gdXNlIHRoZXNlIHZhbHVlcyBhZ2FpbiB3aXRob3V0IGhhdmluZyB0byB0eXBlIG91dCBgYyg0LCAxNiwgOSlgLCB5b3UgY2FuIHNhdmUgaXQgaW4gYSBkYXRhICoqb2JqZWN0KiouIEF0IHRoZSBoZWFydCBvZiBhbG1vc3QgZXZlcnl0aGluZyB5b3Ugd2lsbCBkbyAob3IgYXJlIGV2ZXIgbGlrZWx5IHRvIGRvKSBpbiBSIGlzIHRoZSBjb25jZXB0IHRoYXQgZXZlcnl0aGluZyBpbiBSIGlzIGFuIG9iamVjdC4gVGhlc2Ugb2JqZWN0cyBjYW4gYmUgYWxtb3N0IGFueXRoaW5nLCBmcm9tIGEgc2luZ2xlIG51bWJlciBvciBjaGFyYWN0ZXIgc3RyaW5nIChsaWtlIGEgd29yZCkgdG8gaGlnaGx5IGNvbXBsZXggc3RydWN0dXJlcyBsaWtlIHRoZSBvdXRwdXQgb2YgYSBwbG90LCBhIG1hcCwgYSBzdW1tYXJ5IG9mIHlvdXIgc3RhdGlzdGljYWwgYW5hbHlzaXMgb3IgYSBzZXQgb2YgUiBjb21tYW5kcyB0aGF0IHBlcmZvcm0gYSBzcGVjaWZpYyB0YXNrLg0KDQpZb3UgYXNzaWduIGRhdGEgdG8gYW4gb2JqZWN0IHVzaW5nIHRoZSBhcnJvdyBzaWduIGA8LWAuIFRoaXMgd2lsbCBjcmVhdGUgYW4gb2JqZWN0IGluIFLigJlzIG1lbW9yeSB0aGF0IGNhbiBiZSBjYWxsZWQgYmFjayBpbnRvIHRoZSBjb21tYW5kIHdpbmRvdyBhdCBhbnkgdGltZS4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gc2F2ZSDigJxoZWxsbyB3b3JsZOKAnSB0byBhIHZlY3RvciBjYWxsZWQgKmIqIGJ5IHR5cGluZyBpbg0KYGBge3J9DQpiIDwtICJoZWxsbyB3b3JsZCINCmINCmBgYA0KDQpZb3UgY2FuIHByb25vdW5jZSB0aGUgYWJvdmUgYXMg4oCcYiBiZWNvbWVzIOKAmGhlbGxvIHdvcmxk4oCZ4oCdLg0KDQpUaGUgZmlyc3QgbGluZSB0ZWxscyBSIHRvIHN0b3JlIGIgYXMgJ2hlbGxvIHdvcmxkLicgSW4gdGhlIG5leHQgbGluZSwgd2UgYXJlIHRlbGxpbmcgUiB0byBwcmludCB3aGF0IGIgaXMuDQoNCk5vdGUgdGhhdCBSIGlzICoqY2FzZSBzZW5zaXRpdmUqKiwgaWYgeW91IHR5cGUgaW4gKkIqIGluc3RlYWQgb2YgKmIqLCB5b3Ugd2lsbCBnZXQgYW4gZXJyb3IuDQoNClNpbWlsYXJseSwgeW91IGNhbiBzYXZlIHRoZSBudW1iZXJzIDQsIDE2IGFuZCA5IGludG8gYSB2ZWN0b3IgY2FsbGVkICp2MSouDQoNCmBgYHtyfQ0KdjEgPC0gYyg0LCAxNiwgOSkNCnYxDQpgYGANCg0KXA0KDQpZb3Ugc2hvdWxkIHNlZSB0aGUgb2JqZWN0cyAqYiogYW5kICp2MSogcG9wIHVwIGluIHRoZSBFbnZpcm9ubWVudCB0YWIgb24gdGhlIHRvcCByaWdodCB3aW5kb3cgb2YgeW91ciBSU3R1ZGlvIGludGVyZmFjZS4NCg0KXA0KDQohW0Vudmlyb25tZW50IFdpbmRvd10obGFiMGZpZy5wbmcpDQoNClwNCg0KTm90ZSB0aGF0IHRoZSBuYW1lICp2MSogaXMgbm90aGluZyBzcGVjaWFsIGhlcmUuIFlvdSBjb3VsZCBoYXZlIG5hbWVkIHRoZSBvYmplY3QgKngqIG9yICpzcGgyMTUqIG9yIHlvdXIgcGV04oCZcyBuYW1lIChtaW5lIHdhcyBBbGkgQmFiYSkuIFlvdSBjYW7igJl0LCBob3dldmVyLCBuYW1lIG9iamVjdHMgdXNpbmcgc3BlY2lhbCBjaGFyYWN0ZXJzIChlLmcuICEsIEAsICQpIG9yIG9ubHkgbnVtYmVycyAoYWx0aG91Z2ggeW91IGNhbiBjb21iaW5lIG51bWJlcnMgYW5kIGxldHRlcnMsIGJ1dCBhIG51bWJlciBjYW5ub3QgYmUgYXQgdGhlIGJlZ2lubmluZyBlLmcuICoyZDIqKS4gRm9yIGV4YW1wbGUsIHlvdeKAmWxsIGdldCBhbiBlcnJvciBpZiB5b3Ugc2F2ZSB0aGUgdmVjdG9yICpjKDQsMTYsOSkqIHRvIGFuIG9iamVjdCB3aXRoIHRoZSBmb2xsb3dpbmcgbmFtZXMNCg0KYGBge3IsIHJlc3VsdHM9J2FzaXMnLCBlY2hvPUZBTFNFfQ0KY2F0KCJgYGBgbWFya2Rvd25cbiIsDQogICAgIjEyMyA8LSBjKDQsIDE2LCA5KVxuIiwNCiAgICAiISEhIDwtIGMoNCwgMTYsIDkpXG4iLA0KICAgICJgYGBgIiwgc2VwID0gIiIpDQpgYGANCg0KDQpgYGB7cn0NCiMjIEVycm9yOiA8dGV4dD46Mjo1OiB1bmV4cGVjdGVkIGFzc2lnbm1lbnQNCiMjIDE6IDEyMyA8LSBjKDQsIDE2LCA5KQ0KIyMgMjogISEhIDwtDQojIyAgICAgICAgXg0KYGBgDQoNClwNCg0KQWxzbyBub3RlIHRoYXQgdG8gZGlzdGluZ3Vpc2ggYSBjaGFyYWN0ZXIgdmFsdWUgZnJvbSBhIHZhcmlhYmxlIG5hbWUsIGl0IG5lZWRzIHRvIGJlIHF1b3RlZC4g4oCcdjHigJ0gaXMgYSBjaGFyYWN0ZXIgdmFsdWUgd2hlcmVhcyBgdjFgIGlzIGEgdmFyaWFibGUuIE9uZSBvZiB0aGUgbW9zdCBjb21tb24gbWlzdGFrZXMgZm9yIGJlZ2lubmVycyBpcyB0byBmb3JnZXQgdGhlIHF1b3Rlcy4NCg0KYGBge3IsIHJlc3VsdHM9J2FzaXMnLCBlY2hvPUZBTFNFfQ0KY2F0KCJgYGBgbWFya2Rvd25cbiIsDQogICAgImphbWVzXG4iLA0KICAgICJgYGBgIiwgc2VwID0gIiIpDQpgYGANCg0KYGBge3J9DQojIyAjIyBFcnJvciBpbiBldmFsKGV4cHIsIGVudmlyLCBlbmNsb3MpOiBvYmplY3QgJ2phbWVzJyBub3QgZm91bmQNCmBgYA0KDQpUaGUgZXJyb3Igb2NjdXJzIGJlY2F1c2UgUiB0cmllcyB0byBwcmludCB0aGUgdmFsdWUgb2Ygb2JqZWN0ICpqYW1lcyosIGJ1dCB0aGVyZSBpcyBubyBzdWNoIHZhcmlhYmxlLiBTbyByZW1lbWJlciB0aGF0IGFueSB0aW1lIHlvdSBnZXQgdGhlIGVycm9yIG1lc3NhZ2UgYG9iamVjdCAnc29tZXRoaW5nJyBub3QgZm91bmRgLCB0aGUgbW9zdCBsaWtlbHkgcmVhc29uIGlzIHRoYXQgeW91IGZvcmdvdCB0byBxdW90ZSBhIGNoYXJhY3RlciB2YWx1ZS4gSWYgbm90LCBpdCBwcm9iYWJseSBtZWFucyB0aGF0IHlvdSBoYXZlIG1pc3NwZWxsZWQsIG9yIG5vdCB5ZXQgY3JlYXRlZCwgdGhlIG9iamVjdCB0aGF0IHlvdSBhcmUgcmVmZXJyaW5nIHRvLiBXZeKAmXZlIGluY2x1ZGVkIHRoZSBjb21tb24gcGl0ZmFsbHMgYW5kIFIgdGlwcyBpbiB0aGlzIGNsYXNzIFtyZXNvdXJjZV0oUl9oZWxwXzIwMjYuaHRtbCkuDQoNCkV2ZXJ5IHZlY3RvciBoYXMgdHdvIGtleSBwcm9wZXJ0aWVzOiAqdHlwZSogYW5kICpsZW5ndGgqLiBUaGUgdHlwZSBwcm9wZXJ0eSBpbmRpY2F0ZXMgdGhlIGRhdGEgdHlwZSB0aGF0IHRoZSB2ZWN0b3IgaXMgaG9sZGluZy4gVXNlIHRoZSBjb21tYW5kIGB0eXBlb2YoKWAgdG8gZGV0ZXJtaW5lIHRoZSB0eXBlLg0KDQpgYGB7cn0NCnR5cGVvZihiKQ0KYGBgDQoNClwNCg0KYGBge3J9DQp0eXBlb2YodjEpDQpgYGANCg0KTm90ZSB0aGF0IGEgdmVjdG9yIGNhbm5vdCBob2xkIHZhbHVlcyBvZiBkaWZmZXJlbnQgdHlwZXMuIElmIGRpZmZlcmVudCBkYXRhIHR5cGVzIGV4aXN0LCBSIHdpbGwgY29lcmNlIHRoZSB2YWx1ZXMgaW50byB0aGUgaGlnaGVzdCB0eXBlIGJhc2VkIG9uIGl0cyBpbnRlcm5hbCBoaWVyYXJjaHk6IGxvZ2ljYWwgPCBpbnRlZ2VyIDwgZG91YmxlIDwgY2hhcmFjdGVyLiBUeXBlIGluIGB0ZXN0IDwtIGMoInIiLCA2LCBUUlVFKWAgaW4geW91ciBSIGNvbnNvbGUuIFdoYXQgaXMgdGhlIHZlY3RvciB0eXBlIG9mIGB0ZXN0YD8NCg0KXA0KDQpUaGUgY29tbWFuZCBgbGVuZ3RoKClgIGRldGVybWluZXMgdGhlIG51bWJlciBvZiBkYXRhIHZhbHVlcyB0aGF0IHRoZSB2ZWN0b3IgaXMgc3RvcmluZy4NCg0KYGBge3J9DQpsZW5ndGgoYikNCmBgYA0KDQpgYGB7cn0NCmxlbmd0aCh2MSkNCmBgYA0KDQpZb3UgY2FuIGFsc28gZGlyZWN0bHkgZGV0ZXJtaW5lIGlmIGEgdmVjdG9yIGlzIG9mIGEgc3BlY2lmaWMgZGF0YSB0eXBlIGJ5IHVzaW5nIHRoZSBjb21tYW5kIGBpcy5YKClgIHdoZXJlIHlvdSByZXBsYWNlIGBYYCB3aXRoIHRoZSBkYXRhIHR5cGUuIEZvciBleGFtcGxlLCB0byBmaW5kIG91dCBpZiAqdjEqIGlzIG51bWVyaWMsIHR5cGUgaW46DQoNCmBgYHtyfQ0KaXMubnVtZXJpYyhiKQ0KYGBgDQoNCmBgYHtyfQ0KaXMubnVtZXJpYyh2MSkNCmBgYA0KDQpcDQoNClRoZXJlIGlzIGFsc28gYGlzLmxvZ2ljYWwoKWAsIGBpcy5jaGFyYWN0ZXIoKWAsIGFuZCBgaXMuZmFjdG9yKClgLiBZb3UgY2FuIGFsc28gY29lcmNlIGEgdmVjdG9yIG9mIG9uZSBkYXRhIHR5cGUgdG8gYW5vdGhlci4gRm9yIGV4YW1wbGUsIHNhdmUgdGhlIHZhbHVlIOKAnDHigJ0gYW5kIOKAnDLigJ0gKGJvdGggaW4gcXVvdGVzKSBpbnRvIGEgdmVjdG9yIG5hbWVkICp4MSouDQpgYGB7cn0NCngxIDwtIGMoIjEiLCAiMiIpDQp0eXBlb2YoeDEpDQpgYGANCg0KXA0KDQpUbyBjb252ZXJ0ICp4MSogaW50byBhIG51bWVyaWMsIHVzZSB0aGUgY29tbWFuZCBgYXMubnVtZXJpYygpYA0KYGBge3J9DQp4MiA8LSBhcy5udW1lcmljKHgxKQ0KdHlwZW9mKHgyKQ0KYGBgDQoNClwNCg0KVGhlcmUgaXMgYWxzbyBgYXMubG9naWNhbCgpYCwgYGFzLmNoYXJhY3RlcigpYCwgYW5kIGBhcy5mYWN0b3IoKWAuDQoNCkFuIGltcG9ydGFudCBwcmFjdGljZSB5b3Ugc2hvdWxkIGFkb3B0IGVhcmx5IGlzIHRvIGtlZXAgb25seSBuZWNlc3Nhcnkgb2JqZWN0cyBpbiB5b3VyIGN1cnJlbnQgUiBFbnZpcm9ubWVudC4gRm9yIGV4YW1wbGUsIHdlIHdpbGwgbm90IGJlIHVzaW5nICp4MiogYW55IGxvbmdlciBpbiB0aGlzIGd1aWRlLiBUbyByZW1vdmUgdGhpcyBvYmplY3QgZnJvbSBSIGZvcmV2ZXIsIHVzZSB0aGUgY29tbWFuZCBgcm0oKWANCmBgYHtyfQ0Kcm0oeDIpDQpgYGANCg0KVGhlIGRhdGEgZnJhbWUgb2JqZWN0ICp4Miogc2hvdWxkIGhhdmUgZGlzYXBwZWFyZWQgZnJvbSB0aGUgRW52aXJvbm1lbnQgdGFiLiBBdSByZXZvaXIhIA0KDQpcDQoNCkFsc28gbm90ZSB0aGF0IHdoZW4geW91IGNsb3NlIGRvd24gUiBTdHVkaW8sIHRoZSBvYmplY3RzIHlvdSBjcmVhdGVkIGFib3ZlIHdpbGwgZGlzYXBwZWFyIGZvciBnb29kLiBVbmxlc3MgeW91IHNhdmUgdGhlbSBvbnRvIHlvdXIgaGFyZCBkcml2ZSAod2XigJlsbCB0b3VjaCBvbiBzYXZpbmcgZGF0YSBpbiBsYXRlciBsYWJzKSwgYWxsIGRhdGEgb2JqZWN0cyB5b3UgY3JlYXRlIGluIHlvdXIgY3VycmVudCBSIHNlc3Npb24gd2lsbCBnbyBieWUgYnllIHdoZW4geW91IGV4aXQgdGhlIHByb2dyYW0uDQoNClwNCg0KIyMgRGF0YSBGcmFtZXMNCldlIGxlYXJuZWQgdGhhdCBkYXRhIHZhbHVlcyBjYW4gYmUgc3RvcmVkIGluIGRhdGEgc3RydWN0dXJlcyBrbm93biBhcyB2ZWN0b3JzLiBUaGUgbmV4dCBzdGVwIGlzIHRvIGxlYXJuIGhvdyB0byBzdG9yZSB2ZWN0b3JzIGludG8gYW4gZXZlbiBoaWdoZXIgbGV2ZWwgZGF0YSBzdHJ1Y3R1cmUuIFRoZSBkYXRhIGZyYW1lIGNhbiBkbyB0aGlzLiBEYXRhIGZyYW1lcyBzdG9yZSB2ZWN0b3JzIG9mIHRoZSBzYW1lIGxlbmd0aC4gQ3JlYXRlIGEgdmVjdG9yIGNhbGxlZCB2MiBzdG9yaW5nIHRoZSB2YWx1ZXMgNSwgMTIsIGFuZCAyNS4NCmBgYHtyfQ0KdjIgPC0gYyg1LDEyLDI1KQ0KYGBgDQoNClwNCg0KV2UgY2FuIGNyZWF0ZSBhIGRhdGEgZnJhbWUgdXNpbmcgdGhlIGNvbW1hbmQgYGRhdGEuZnJhbWUoKWAgc3RvcmluZyB0aGUgdmVjdG9ycyAqdjEqIGFuZCAqdjIqIGFzIGNvbHVtbnMuDQpgYGB7cn0NCmRhdGEuZnJhbWUodjEsdjIpDQpgYGANCg0KXA0KDQpTdG9yZSB0aGlzIGRhdGEgZnJhbWUgaW4gYW4gb2JqZWN0IGNhbGxlZCBkZjENCmBgYHtyfQ0KZGYxPC1kYXRhLmZyYW1lKHYxLCB2MikNCmBgYA0KDQpkZjEgc2hvdWxkIHBvcCB1cCBpbiB5b3VyIEVudmlyb25tZW50IHdpbmRvdy4gWW914oCZbGwgbm90aWNlIGEgIVtsYWIwZmlnMi5wbmddKGxhYjBmaWcyLnBuZykgbmV4dCB0byAqZGYxKi4gVGhpcyB0ZWxscyB5b3UgdGhhdCAqZGYxKiBwb3NzZXNzZXMgb3IgaG9sZHMgbW9yZSB0aGFuIG9uZSBvYmplY3QuIENsaWNrIG9uICFbbGFiMGZpZzIucG5nXShsYWIwZmlnMi5wbmcpIGFuZCB5b3XigJlsbCBzZWUgdGhlIHR3byB2ZWN0b3JzIHdlIHNhdmVkIGludG8gKmRmMSouIEFub3RoZXIgbmljZSB0aGluZyB5b3UgY2FuIGRvIGlzIGRpcmVjdGx5IGNsaWNrIG9uICpkZjEqIGZyb20gdGhlIEVudmlyb25tZW50IHdpbmRvdyB0byBicmluZyB1cCBhbiBFeGNlbCBzdHlsZSB3b3Jrc2hlZXQgb24gdGhlIHRvcCBsZWZ0IG9mIHlvdXIgUlN0dWRpbyBpbnRlcmZhY2UuIFlvdSBjYW4gYWxzbyB0eXBlIGluOg0KYGBge3J9DQpWaWV3KGRmMSkNCmBgYA0KdG8gYnJpbmcgdGhlIHdvcmtzaGVldCB1cC4gWW91IGNhbuKAmXQgZWRpdCB0aGlzIHdvcmtzaGVldCBkaXJlY3RseSwgYnV0IGl0IGFsbG93cyB5b3UgdG8gc2VlIHRoZSB2YWx1ZXMgdGhhdCBhIGhpZ2hlciBsZXZlbCBSIGRhdGEgb2JqZWN0IGNvbnRhaW5zLg0KDQpcDQoNCldlIGNhbiBzdG9yZSBkaWZmZXJlbnQgdHlwZXMgb2YgdmVjdG9ycyBpbiBhIGRhdGEgZnJhbWUuIEZvciBleGFtcGxlLCB3ZSBjYW4gc3RvcmUgb25lIGNoYXJhY3RlciB2ZWN0b3IgYW5kIG9uZSBudW1lcmljIHZlY3RvciBpbiBhIHNpbmdsZSBkYXRhIGZyYW1lLg0KYGBge3J9DQp2MyA8LSBjKCJjYXRoZXJpbmUiLCAiZGVjbGFuIiwgImd3ZW4iKQ0KZGYyIDwtIGRhdGEuZnJhbWUodjEsIHYzKQ0KZGYyDQpgYGANCg0KXA0KDQpGb3IgaGlnaGVyIGxldmVsIGRhdGEgc3RydWN0dXJlcyBsaWtlIGEgZGF0YSBmcmFtZSwgdXNlIHRoZSBmdW5jdGlvbiBgY2xhc3MoKWAgdG8gZmlndXJlIG91dCB3aGF0IGtpbmQgb2Ygb2JqZWN0IHlvdeKAmXJlIHdvcmtpbmcgd2l0aC4NCmBgYHtyfQ0KY2xhc3MoZGYyKQ0KYGBgDQoNClwNCg0KV2UgY2Fu4oCZdCB1c2UgYGxlbmd0aCgpYCBvbiBhIGRhdGEgZnJhbWUgYmVjYXVzZSBpdCBoYXMgbW9yZSB0aGFuIG9uZSB2ZWN0b3IuIEluc3RlYWQsIGl0IGhhcyAqZGltZW5zaW9ucyogLSB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMuIFlvdSBjYW4gZmluZCB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMgdGhhdCBhIGRhdGEgZnJhbWUgaGFzIGJ5IHVzaW5nIHRoZSBjb21tYW5kIGBkaW0oKWANCmBgYHtyfQ0KZGltKGRmMSkNCmBgYA0KSGVyZSwgdGhlIGRhdGEgZnJhbWUgKmRmMSogaGFzIDMgcm93cyBhbmQgMiBjb2x1bW5zLiBEYXRhIGZyYW1lcyBhbHNvIGhhdmUgY29sdW1uIG5hbWVzLCB3aGljaCBhcmUgY2hhcmFjdGVycy4NCg0KXA0KDQpXZSBjYW4gZmlndXJlIG91dCB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgdXNpbmcgYGNvbG5hbWVzYC4NCmBgYHtyfQ0KY29sbmFtZXMoZGYxKQ0KYGBgDQoNCkluIHRoaXMgY2FzZSwgdGhlIGRhdGEgZnJhbWUgdXNlZCB0aGUgdmVjdG9yIG5hbWVzIGZvciB0aGUgY29sdW1uIG5hbWVzLg0KDQpcDQoNCldlIGNhbiBleHRyYWN0IGNvbHVtbnMgZnJvbSBkYXRhIGZyYW1lcyBieSByZWZlcnJpbmcgdG8gdGhlaXIgbmFtZXMgdXNpbmcgdGhlIGAkYCBzaWduLg0KYGBge3J9DQpkZjEkdjENCmBgYA0KDQpcDQoNCldlIGNhbiBhbHNvIGV4dHJhIGRhdGEgZnJvbSBkYXRhIGZyYW1lcyB1c2luZyBicmFja2V0cyBbICwgXQ0KYGBge3J9DQpkZjFbLDFdDQpgYGANCg0KVGhlIHZhbHVlIGJlZm9yZSB0aGUgY29tbWEgaW5kaWNhdGVzIHRoZSByb3csIHdoaWNoIHlvdSBsZWF2ZSBlbXB0eSBpZiB5b3UgYXJlIG5vdCBzZWxlY3RpbmcgYnkgcm93LCB3aGljaCB3ZSBkaWQgYWJvdmUuIFRoZSB2YWx1ZSBhZnRlciB0aGUgY29tbWEgaW5kaWNhdGVzIHRoZSBjb2x1bW4sIHdoaWNoIHlvdSBsZWF2ZSBlbXB0eSBpZiB5b3UgYXJlIG5vdCBzZWxlY3RpbmcgYnkgY29sdW1uLiBUaGUgYWJvdmUgbGluZSBvZiBjb2RlIHNlbGVjdGVkIHRoZSBmaXJzdCBjb2x1bW4uIA0KDQpcDQoNCkxldOKAmXMgbm93IHNlbGVjdCB0aGUgMm5kIHJvdy4NCmBgYHtyfQ0KZGYxWzIsXQ0KYGBgDQoNClwNCg0KT0ssIHNvIHRoYXQgd2Fzbid0IHRvbyBoYXJkLiBOb3cgbGV0J3MgdHJ5IHNvbWV0aGluZyBhIGxpdHRsZSB0cmlja2llciEgV2hhdCBpcyB0aGUgdmFsdWUgaW4gdGhlIDJuZCByb3cgYW5kIDFzdCBjb2x1bW4/DQpgYGB7cn0NCmRmMVsyLDFdDQpgYGANCg0KU2VlIC0tIHdlIGNhbiBkbyBoYXJkIHRoaW5ncyENCg0KXA0KDQojIEZ1bmN0aW9ucw0KTGV04oCZcyB0YWtlIGEgc3RlcCBiYWNrIGFuZCB0YWxrIGFib3V0IGZ1bmN0aW9ucyAoYWxzbyBrbm93biBhcyBjb21tYW5kcyBvciBtYWNyb3MgKGluIFNBUykpLiBBbiBSIGZ1bmN0aW9uIGlzIGEgcGFja2FnZWQgcmVjaXBlIHRoYXQgY29udmVydHMgb25lIG9yIG1vcmUgaW5wdXRzIChjYWxsZWQgYXJndW1lbnRzKSBpbnRvIGEgc2luZ2xlIG91dHB1dC4gWW91IGV4ZWN1dGUgYWxsIG9mIHlvdXIgdGFza3MgaW4gUiB1c2luZyBmdW5jdGlvbnMuIFdlIGhhdmUgYWxyZWFkeSB1c2VkIGEgY291cGxlIG9mIGZ1bmN0aW9ucyBhYm92ZSBpbmNsdWRpbmcgYHR5cGVvZigpYCBhbmQgYGNvbG5hbWVzKClgLiBFdmVyeSBmdW5jdGlvbiBpbiBSIHdpbGwgaGF2ZSB0aGUgZm9sbG93aW5nIGJhc2ljIGZvcm1hdA0KDQpgZnVuY3Rpb25OYW1lKGFyZzEgPSB2YWwxLCBhcmcyID0gdmFsMiwgLi4uKWANCg0KSW4gUiwgeW91IHR5cGUgaW4gdGhlIGZ1bmN0aW9u4oCZcyBuYW1lIGFuZCBzZXQgYSBudW1iZXIgb2Ygb3B0aW9ucyBvciBwYXJhbWV0ZXJzIHdpdGhpbiBwYXJlbnRoZXNlcyB0aGF0IGFyZSBzZXBhcmF0ZWQgYnkgY29tbWFzLiBTb21lIG9wdGlvbnMgbmVlZCB0byBiZSBzZXQgYnkgdGhlIHVzZXIgLSBpLmUuIHRoZSBmdW5jdGlvbiB3aWxsIHNwaXQgb3V0IGFuIGVycm9yIGJlY2F1c2UgYSByZXF1aXJlZCBvcHRpb24gaXMgYmxhbmsgLSB3aGVyZWFzIG90aGVycyBjYW4gYmUgc2V0IGJ1dCBhcmUgbm90IHJlcXVpcmVkIGJlY2F1c2UgdGhlcmUgaXMgYSBkZWZhdWx0IHZhbHVlIGVzdGFibGlzaGVkLg0KDQpMZXTigJlzIHVzZSB0aGUgZnVuY3Rpb24gYHNlcSgpYCB3aGljaCBtYWtlcyByZWd1bGFyIHNlcXVlbmNlcyBvZiBudW1iZXJzLiBZb3UgY2FuIGZpbmQgb3V0IHdoYXQgdGhlIG9wdGlvbnMgYXJlIGZvciBhIGZ1bmN0aW9uIGJ5IGNhbGxpbmcgdXAgaXRzIGhlbHAgZG9jdW1lbnRhdGlvbiBieSB0eXBpbmcgYD9gIGFuZCB0aGUgZnVuY3Rpb24gbmFtZQ0KDQpgYGB7cn0NCj8gc2VxDQpgYGANCg0KVGhlIGhlbHAgZG9jdW1lbnRhdGlvbiBzaG91bGQgcG9wIHVwIGluIHRoZSBib3R0b20gcmlnaHQgd2luZG93IG9mIHlvdXIgUlN0dWRpbyBpbnRlcmZhY2UuIFRoZSBkb2N1bWVudGF0aW9uIHNob3VsZCBhbHNvIHByb3ZpZGUgc29tZSBleGFtcGxlcyBvZiB0aGUgZnVuY3Rpb24gYXQgdGhlIGJvdHRvbSBvZiB0aGUgcGFnZS4gVHlwZSB0aGUgYXJndW1lbnRzIGZyb20gPSAxLCB0byA9IDEwIGluc2lkZSB0aGUgcGFyZW50aGVzZXMuDQoNCmBgYHtyfQ0Kc2VxKGZyb20gPSAxLCB0byA9IDEwKQ0KYGBgDQoNClwNCg0KWW91IHNob3VsZCBnZXQgdGhlIHNhbWUgcmVzdWx0IGlmIHlvdSB0eXBlIGluOg0KYGBge3J9DQpzZXEoMSwgMTApDQpgYGANCg0KVGhlIGNvZGUgYWJvdmUgZGVtb25zdHJhdGVzIHNvbWV0aGluZyBhYm91dCBob3cgUiByZXNvbHZlcyBmdW5jdGlvbiBhcmd1bWVudHMuIFdoZW4geW91IHVzZSBhIGZ1bmN0aW9uLCB5b3UgY2FuIGFsd2F5cyBzcGVjaWZ5IGFsbCB0aGUgYXJndW1lbnRzIGluIGBhcmcgPSB2YWx1ZWAgZm9ybS4gQnV0IGlmIHlvdSBkbyBub3QsIFIgYXR0ZW1wdHMgdG8gcmVzb2x2ZSBieSBwb3NpdGlvbi4gU28gaW4gdGhlIGNvZGUgYWJvdmUsIGl0IGlzIGFzc3VtZWQgdGhhdCB3ZSB3YW50IGEgc2VxdWVuY2UgYGZyb20gPSAxYCB0aGF0IGdvZXMgYHRvID0gMTBgIGJlY2F1c2Ugd2UgdHlwZWQgMSBiZWZvcmUgMTAuIFR5cGUgaW4gMTAgYmVmb3JlIDEgYW5kIHNlZSB3aGF0IGhhcHBlbnMuIFNpbmNlIHdlIGRpZG7igJl0IHNwZWNpZnkgc3RlcCBzaXplLCB0aGUgZGVmYXVsdCB2YWx1ZSBvZiBgYnlgIGluIHRoZSBmdW5jdGlvbiBkZWZpbml0aW9uIGlzIHVzZWQsIHdoaWNoIGVuZHMgdXAgYmVpbmcgMSBpbiB0aGlzIGNhc2UuDQoNClwNCg0KIyBQYWNrYWdlcw0KDQpGdW5jdGlvbnMgZG8gbm90IGV4aXN0IGluIGEgdmFjdXVtLCBidXQgZXhpc3Qgd2l0aGluIFtSIHBhY2thZ2VzXShodHRwczovL3ItcGtncy5vcmcvaW50cm8uaHRtbCkuIFBhY2thZ2VzIGFyZSB0aGUgZnVuZGFtZW50YWwgdW5pdHMgb2YgcmVwcm9kdWNpYmxlIFIgY29kZS4gVGhleSBpbmNsdWRlIHJldXNhYmxlIFIgZnVuY3Rpb25zLCB0aGUgZG9jdW1lbnRhdGlvbiB0aGF0IGRlc2NyaWJlcyBob3cgdG8gdXNlIHRoZW0sIGFuZCBzYW1wbGUgZGF0YS4gQXQgdGhlIHRvcCBsZWZ0IG9mIGEgZnVuY3Rpb27igJlzIGhlbHAgZG9jdW1lbnRhdGlvbiwgeW914oCZbGwgZmluZCBpbiBjdXJseSBicmFja2V0cyB0aGUgUiBwYWNrYWdlIHRoYXQgdGhlIGZ1bmN0aW9uIGlzIGhvdXNlZCBpbi4gRm9yIGV4YW1wbGUsIHR5cGUgaW4geW91ciBjb25zb2xlIGA/IHNlcWAuIEF0IHRoZSB0b3AgcmlnaHQgb2YgdGhlIGhlbHAgZG9jdW1lbnRhdGlvbiwgeW914oCZbGwgZmluZCB0aGF0IGBzZXEoKWAgaXMgaW4gdGhlIHBhY2thZ2UgKipiYXNlKiouIEFsbCB0aGUgZnVuY3Rpb25zIHdlIGhhdmUgdXNlZCBzbyBmYXIgYXJlIHBhcnQgb2YgcGFja2FnZXMgdGhhdCBoYXZlIGJlZW4gcHJlLWluc3RhbGxlZCBhbmQgcHJlLWxvYWRlZCBpbnRvIFIuDQoNCkluIG9yZGVyIHRvIHVzZSBmdW5jdGlvbnMgaW4gYSBuZXcgcGFja2FnZSwgeW91IGZpcnN0IG5lZWQgdG8gaW5zdGFsbCB0aGUgcGFja2FnZSB1c2luZyB0aGUgYGluc3RhbGwucGFja2FnZXMoKWAgY29tbWFuZC4gRm9yIGV4YW1wbGUsIHdlIHdpbGwgYmUgdXNpbmcgY29tbWFuZHMgZnJvbSB0aGUgcGFja2FnZSAqKnRpZHl2ZXJzZSoqIGluIHRoaXMgbGFiLg0KDQpgYGB7cn0NCm9wdGlvbnMocmVwb3MgPSBjKENSQU4gPSAiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnIikpDQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KYGBgDQpZb3Ugc2hvdWxkIHNlZSBhIGJ1bmNoIG9mIGdpYmJlcmlzaCByb2xsIHRocm91Z2ggeW91ciBjb25zb2xlIHNjcmVlbi4gRG9u4oCZdCB3b3JyeSwgdGhhdOKAmXMganVzdCBSIGRvd25sb2FkaW5nIGFsbCBvZiB0aGUgb3RoZXIgcGFja2FnZXMgYW5kIGFwcGxpY2F0aW9ucyB0aGF0ICoqdGlkeXZlcnNlKiogcmVsaWVzIG9uLiBUaGVzZSBhcmUga25vd24gYXMgW2RlcGVuZGVuY2llcy5dKGh0dHBzOi8vci1wa2dzLm9yZy9kZXBlbmRlbmNpZXMtbWluZHNldC1iYWNrZ3JvdW5kLmh0bWwpIFVubGVzcyB5b3UgZ2V0IGEgbWVzc2FnZSBpbiByZWQgdGhhdCBpbmRpY2F0ZXMgdGhlcmUgaXMgYW4gZXJyb3IgKGxpa2Ugd2Ugc2F3IHdoZW4gd2UgdHlwZWQgaW4g4oCcaGVsbG8gd29ybGTigJ0gd2l0aG91dCBxdW90ZXMpLCB5b3Ugc2hvdWxkIGJlIGZpbmUuDQoNCk5leHQsIHlvdSB3aWxsIG5lZWQgdG8gbG9hZCBwYWNrYWdlcyBpbiB5b3VyIHdvcmtpbmcgZW52aXJvbm1lbnQgKGV2ZXJ5IHRpbWUgeW91IHN0YXJ0IFJTdHVkaW8pLiBXZSBkbyB0aGlzIHdpdGggdGhlIGBsaWJyYXJ5KClgIGZ1bmN0aW9uLiBOb3RpY2UgdGhlcmUgYXJlIG5vIHF1b3RlcyBhcm91bmQgKip0aWR5dmVyc2UqKiB0aGlzIHRpbWUgKGp1c3QgdG8gbWFrZSB0aGluZ3MgdHJpY2tpZXIgZm9yIHVzISkuDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANClRoZSBQYWNrYWdlcyB3aW5kb3cgYXQgdGhlIGxvd2VyLXJpZ2h0IG9mIHlvdXIgUlN0dWRpbyBzaG93cyB5b3UgYWxsIHRoZSBwYWNrYWdlcyB5b3UgY3VycmVudGx5IGhhdmUgaW5zdGFsbGVkLiBJZiB5b3UgZG9u4oCZdCBoYXZlIGEgcGFja2FnZSBsaXN0ZWQgaW4gdGhpcyB3aW5kb3csIHlvdeKAmWxsIG5lZWQgdG8gdXNlIHRoZSBgaW5zdGFsbC5wYWNrYWdlcygpYCBmdW5jdGlvbiB0byBpbnN0YWxsIGl0LiBJZiB0aGUgcGFja2FnZSBpcyBjaGVja2VkLCB0aGF0IG1lYW5zIGl0IGlzIGxvYWRlZCBpbnRvIHlvdXIgY3VycmVudCBSIHNlc3Npb24NCg0KRm9yIGV4YW1wbGUsIGhlcmUgaXMgYSBzZWN0aW9uIG9mIG15IFBhY2thZ2VzIHdpbmRvdw0KIVt3aW5kb3cxLnBuZ10od2luZG93MS5wbmcpDQoNClwNCg0KVGhlIG9ubHkgcGFja2FnZXMgbG9hZGVkIGludG8gbXkgY3VycmVudCBzZXNzaW9uIGlzICoqbWV0aG9kcyoqLCBhIHBhY2thZ2UgdGhhdCBpcyBsb2FkZWQgZXZlcnkgdGltZSB5b3Ugb3BlbiBhbiBSIHNlc3Npb24uIExldOKAmXMgc2F5IEkgdXNlIGBpbnN0YWxsLnBhY2thZ2VzKClgIHRvIGluc3RhbGwgdGhlIHBhY2thZ2UgKiptYXRyaXhTdGF0cyoqLiBUaGUgd2luZG93IG5vdyBsb29rcyBsaWtlOg0KIVt3aW5kb3cyLnBuZ10od2luZG93Mi5wbmcpDQoNClwNCg0KTGV0J3MgbG9hZCAqKm1hdHJpeFN0YXRzKiogdXNpbmcgYGxpYnJhcnkoKWAsIGFuZCB0aGVuIHdlIHdpbGwgc2VlIGEgY2hlY2sgbWFyayBhcHBlYXJzIG5leHQgdG8gKiptYXRyaXhTdGF0cyoqLg0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoIm1hdHJpeFN0YXRzIikNCmxpYnJhcnkobWF0cml4U3RhdHMpDQpgYGANCg0KDQpcDQoNCkxvb2sgYXQgdXMhDQoNClRvIHVuaW5zdGFsbCBhIHBhY2thZ2UsIHVzZSB0aGUgZnVuY3Rpb24gYHJlbW92ZS5wYWNrYWdlcygpYC4NCg0KKipOb3RlIHRoYXQgeW91IG9ubHkgbmVlZCB0byBpbnN0YWxsIHBhY2thZ2VzIG9uY2Ugd2l0aCBgaW5zdGFsbC5wYWNrYWdlcygpYCwgYnV0IHlvdSBuZWVkIHRvIGxvYWQgdGhlbSBlYWNoIHRpbWUgeW91IHJlbGF1bmNoIFJTdHVkaW8gd2l0aCBgbGlicmFyeSgpYC4qKiBSZXBlYXQgYWZ0ZXIgbWU6IEluc3RhbGwgb25jZSwgbGlicmFyeSBldmVyeSB0aW1lLiBJZiB5b3UgbmVlZCB0byByZWluc3RhbGwgUiBvciB1cGRhdGUgdG8gYSBuZXcgdmVyc2lvbiBvZiBSLCB5b3Ugd2lsbCBuZWVkIHRvIHJlaW5zdGFsbCBhbGwgcGFja2FnZXMuIEFuZCBhcyBub3RlZCBlYXJsaWVyLCBSIGhhcyBzZXZlcmFsIHBhY2thZ2VzIGFscmVhZHkgcHJlbG9hZGVkIGludG8geW91ciB3b3JraW5nIGVudmlyb25tZW50LiBUaGVzZSBhcmUga25vd24gYXMgKipiYXNlKiogcGFja2FnZXMgYW5kIGEgbGlzdCBvZiB0aGVpciBmdW5jdGlvbnMgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3N0YXQuZXRoei5jaC9SLW1hbnVhbC9SLWRldmVsL2xpYnJhcnkvYmFzZS9odG1sLzAwSW5kZXguaHRtbCkuIA0KDQpcDQoNCiMgVGlkeXZlcnNlDQoNCkluIG1vc3QgbGFicywgd2Ugd2lsbCBiZSB1c2luZyBjb21tYW5kcyBmcm9tIHRoZSAqKnRpZHl2ZXJzZSoqIHBhY2thZ2UuIFtUaWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKSBpcyBhIGNvbGxlY3Rpb24gb2YgaGlnaC1wb3dlcmVkLCBjb25zaXN0ZW50LCBhbmQgZWFzeS10by11c2UgcGFja2FnZXMgZGV2ZWxvcGVkIGJ5IGEgbnVtYmVyIG9mIHRob3VnaHRmdWwgYW5kIHRhbGVudGVkIFIgZGV2ZWxvcGVycy4gVGhlIGNvbnNpc3RlbmN5IG9mIHRoZSAqKnRpZHl2ZXJzZSoqLCB0b2dldGhlciB3aXRoIHRoZSBnb2FsIG9mIGluY3JlYXNpbmcgcHJvZHVjdGl2aXR5LCBtZWFuIHRoYXQgdGhlIHN5bnRheCBvZiB0aWR5IGZ1bmN0aW9ucyBpcyB0eXBpY2FsbHkgc3RyYWlnaHRmb3J3YXJkIHRvIGxlYXJuLiBZb3UgY2FuIHJlYWQgbW9yZSBhYm91dCAqKnRpZHl2ZXJzZSoqIHByaW5jaXBsZXMgaW4gQ2hhcHRlciA5LCBwYWdlcyAxNDctMTUxIGluIFJEUy4NCg0KRXhjaXRlZCBhYm91dCBlbnRlcmluZyB0aGUgdGlkeXZlcnNlPyBJIGJldCB5b3UgYXJlLCBzbyBoZXJlIGlzIGEgYmFkZ2UgdG8gc2hvdyB5b3VyIGV4Y2l0ZW1lbnQhDQoNCiFbWW91ciBUaWR5dmVyc2UgQmFkZ2VdKHRpZHl2ZXJzZS5wbmcpDQoNClwNCg0KIyMgVGliYmxlcw0KDQpBbHRob3VnaCB0aGUgKip0aWR5dmVyc2UqKiB3b3JrcyB3aXRoIGFsbCBkYXRhIG9iamVjdHMsIGl0cyBmdW5kYW1lbnRhbCBvYmplY3QgdHlwZSBpcyB0aGUgdGliYmxlLiBUaWJibGVzIGFyZSBub3Qgb25seSBhIHN1cGVyIGZ1biB3b3JkIHRvIHNheSwgdGhleSBhcmUgZGF0YSBmcmFtZXMgdGhhdCB0d2VhayBzb21lIG9sZGVyIGJlaGF2aW9ycyB0byBtYWtlIGxpZmUgYSBsaXR0bGUgZWFzaWVyLiBUaGVyZSBhcmUgdHdvIG1haW4gZGlmZmVyZW5jZXMgaW4gdGhlIHVzYWdlIG9mIGEgZGF0YSBmcmFtZSB2cyBhIHRpYmJsZTogcHJpbnRpbmcgYW5kIHN1YnNldHRpbmcuIExldOKAmXMgYmUgY2xlYXIgaGVyZSAtLSB0aWJibGVzIGFyZSBqdXN0IGEgc3BlY2lhbCBraW5kIG9mIGRhdGEgZnJhbWUuIFRoZXkganVzdCBtYWtlIHRoaW5ncyDigJx0aWRpZXIu4oCdIExldOKAmXMgYnJpbmcgaW4gc29tZSBkYXRhIHRvIGlsbHVzdHJhdGUgdGhlIGRpZmZlcmVuY2VzIGFuZCBzaW1pbGFyaXRpZXMgYmV0d2VlbiBkYXRhIGZyYW1lcyBhbmQgdGliYmxlcy4gSW5zdGFsbCB0aGUgcGFja2FnZSAqKm55Y2ZsaWdodHMxMyoqDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygibnljZmxpZ2h0czEzIikNCmBgYA0KDQpcDQoNCk1ha2Ugc3VyZSB5b3UgYWxzbyBsb2FkIHRoZSBwYWNrYWdlLg0KDQpgYGB7cn0NCmxpYnJhcnkobnljZmxpZ2h0czEzKQ0KYGBgDQoNClwNCg0KSWYgeW91IGxvb2sgaW4gdGhlIHVwcGVyIHJpZ2h0IGhhbmQgKkVudmlyb25tZW50KiB0YWIgYW5kIGNsaWNrIG9uICpHbG9iYWwgRW52aXJvbm1lbnQqLCB5b3Ugd2lsbCBzZWUgdGhlcmUgaXMgYSBkYXRhc2V0IGNhbGxlZCAqKmZsaWdodHMqKiBpbmNsdWRlZCBpbiB0aGlzIHBhY2thZ2UuIEl0IGluY2x1ZGVzIGluZm9ybWF0aW9uIG9uIGFsbCAzMzYsNzc2IGZsaWdodHMgdGhhdCBkZXBhcnRlZCBmcm9tIE5ldyBZb3JrIENpdHkgaW4gMjAxMy4gTGV04oCZcyBzYXZlIHRoaXMgZmlsZSBpbiB0aGUgbG9jYWwgUiBlbnZpcm9ubWVudC4NCg0KYGBge3J9DQpueWN0aWJibGUgPC0gZmxpZ2h0cw0KY2xhc3MobnljdGliYmxlKQ0KYGBgDQoNClwNCg0KVGhpcyBkYXRhc2V0IGlzIGEgdGliYmxlLiBMZXTigJlzIGFsc28gc2F2ZSBpdCBhcyBhIHJlZ3VsYXIgZGF0YSBmcmFtZSBieSB1c2luZyB0aGUgYGFzLmRhdGEuZnJhbWUoKWAgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KbnljZGYgPC0gYXMuZGF0YS5mcmFtZShmbGlnaHRzKQ0KY2xhc3MobnljZGYpDQpgYGANCg0KXA0KDQpUaGUgZmlyc3QgZGlmZmVyZW5jZSBiZXR3ZWVuIGRhdGEgZnJhbWVzIGFuZCB0aWJibGVzIGlzIGhvdyB0aGUgZGF0YXNldCBsb29rcy4gVGliYmxlcyBoYXZlIGEgcmVmaW5lZCBwcmludCBtZXRob2QgdGhhdCBzaG93cyBvbmx5IHRoZSBmaXJzdCAxMCByb3dzLCBhbmQgb25seSB0aGUgY29sdW1ucyB0aGF0IGZpdCBvbiB0aGUgc2NyZWVuLiBJbiBhZGRpdGlvbiwgZWFjaCBjb2x1bW4gcmVwb3J0cyBpdHMgbmFtZSBhbmQgdHlwZS4NCg0KYGBge3J9DQpueWN0aWJibGUNCmBgYA0KDQpcDQoNClRpYmJsZXMgYXJlIGRlc2lnbmVkIHNvIHRoYXQgeW91IGRvbuKAmXQgb3ZlcndoZWxtIHlvdXIgY29uc29sZSB3aGVuIHlvdSBwcmludCBsYXJnZSBkYXRhIGZyYW1lcy4gQ29tcGFyZSB0aGUgcHJpbnQgb3V0cHV0IGFib3ZlIHRvIHdoYXQgeW91IGdldCB3aXRoIGEgZGF0YSBmcmFtZS4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCm55Y2RmDQpgYGANCg0KXA0KDQpVbSwgdGhhdCB3YXMgYSBsb3QuLi4uVGliYmxlIGlzIG11Y2ggY2xlYW5lci4gWW91IGNhbiBicmluZyB1cCB0aGUgRXhjZWwgbGlrZSB3b3Jrc2hlZXQgb2YgdGhlIHRpYmJsZSAob3IgZGF0YSBmcmFtZSkgdXNpbmcgdGhlIGBWaWV3KClgIGZ1bmN0aW9uLg0KYGBge3J9DQpWaWV3KG55Y3RpYmJsZSkNCmBgYA0KDQpcDQoNCllvdSBjYW4gaWRlbnRpZnkgdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zIChhbmQgaGVuY2UgdGhlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCkgYnkgdXNpbmcgdGhlIGZ1bmN0aW9uIGBuYW1lcygpYC4NCmBgYHtyfQ0KbmFtZXMobnljdGliYmxlKQ0KYGBgDQpUaG9zZSBtYXkgY29tZSBpbiBoYW5keSBpZiB3ZSB3YW50ZWQgdG8gYW5hbHl6ZSB0aGUgZGF0YSENCg0KXA0KDQpGaW5hbGx5LCBsZXQncyBjb252ZXJ0IGEgcmVndWxhciBkYXRhIGZyYW1lIHRvIGEgdGliYmxlIHVzaW5nIHRoZSBgYXNfdGliYmxlKClgIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCmFzX3RpYmJsZShueWNkZikNCmBgYA0KDQpOb3QgYWxsIGZ1bmN0aW9ucyB3b3JrIHdpdGggdGliYmxlcywgcGFydGljdWxhcmx5IHRob3NlIHRoYXQgYXJlIHNwZWNpZmljIHRvIHNwYXRpYWwgZGF0YS4gQXMgc3VjaCwgd2XigJlsbCBiZSB1c2luZyBhIGNvbWJpbmF0aW9uIG9mIHRpYmJsZXMgYW5kIHJlZ3VsYXIgZGF0YSBmcmFtZXMgdGhyb3VnaG91dCB0aGUgY2xhc3MsIHdpdGggYSBwcmVmZXJlbmNlIHRvd2FyZHMgdGliYmxlcyB3aGVyZSBwb3NzaWJsZS4gTm90ZSB0aGF0IHdoZW4geW91IHNlYXJjaCBvbiBHb29nbGUgZm9yIGhvdyB0byBkbyBzb21ldGhpbmcgaW4gUiwgeW91IHdpbGwgbGlrZWx5IGdldCAqKm5vbi10aWR5IHdheXMqKiBvZiBkb2luZyB0aGluZ3MuIE1vc3Qgb2YgdGhlc2Ugc3VnZ2VzdGlvbnMgYXJlIGZpbmUsIGJ1dCBzb21lIGFyZSBub3QgYW5kIG1heSBzY3JldyB5b3UgdXAgZG93biB0aGUgcm9hZC4gTXkgYWR2aWNlIGlzIHRvIHRyeSB0byBzdGljayB3aXRoIHRpZHkgZnVuY3Rpb25zIHRvIGRvIHRoaW5ncyBpbiBSLg0KDQpBbnl3YXksIHlvdSBlYXJuZWQgYW5vdGhlciBiYWRnZS4gWWVzIQ0KDQohW1lvdXIgVGliYmxlIEJhZGdlXSh0aWJibGUucG5nKQ0KDQpcDQoNCg0K