Session 01: Feeling R: Basics + Intuitive Understanding of R
Feedback should be send to goran.milovanovic@datakolektiv.com
. These notebooks accompany the Intro to Data Science: Non-Technical Background course 2020/21.
Welcome to R!
What do we want to do today?
Our goal in Session 01
is to develop and intuition of R, and a feeling for the programming language R. Yes, we need to work on our emotions, dear students. Because programming in Data Science - as well as programming in general - is nothing but communication, and a very specific one. Programming languages are not very tolerant in the way they communicate with us: for example, they demand that we be very upfront and straightforward, and they have no tolerance for ambiguity. If they do not understand something that we say, they will immediately respond with a cold - and at first cryptic - error message. That means that we need to be very patient while we learn the strict rules of communication with machines in Data Science and elsewhere. I guarantee you that there is a specific emotional state, a specific state of mind, that accompanies the focused work of any programmer and Data Scientist. It is a kind of calmness coupled with a good, very good intuition about the nature of the programming language they use. This is our goal: we need to start developing our intuition about R. How does R like to work?
0. Prerequisits.
None. There is an R console open in front of your eyes and you can reach your machine’s keyboard.
1. Navigating the work environment: where am I?
Everything happens in folders (or directories - simply pick the term of your preference), right? Say, we want to write our first R program - a script, as it is usually called. That program will consist of a set of lines in the programming language R, and those lines will be telling to our machine what to we want from her. Ok, this set of lines - a script - needs to live somewhere. It needs to live in some directory on your computer’s disk.
When we work in R, there is always something called a working directory. There is a function in R - think of it as a free R program that you already have upon installing this programming language - that will let you know what is your current working directory. If you write any R code and decide to save it for later use, and do not specify the directory where you want it to live, R will use your current working directory as a destination folder. That function is get_wd()
getwd()
[1] "/Users/goransm/Work/___DataKolektiv/_EDU/DSSVol01_IntroRNonTech2021-22/_Code/IntroDataScience_NonTech_S01"
Note. As you can see, the file paths in R are a bit different that those that you are used to see on the Windows operative system (but exactly the same as those we have on Linux). R will use /
to define paths, while Windows uses the backslash - \
- instead. Your RStudio IDE takes care that these paths are compatible so there is nothing you need to worry about (for now).
The output of getwd()
should end with 01_IntroDataScience_Non-Tech/_Code
if you have opened the course RStudio project exactly as suggested in the beginning of this session. What comes before 01_IntroDataScience_Non-Tech/_Code
is the path on your local filesystem (i.e. your machine) where you have cloned the course repository.
Now, let’s learn about the contents of our current working directory:
list.files(getwd())
[1] "IntroDataScience_NonTech_S01.nb.html" "IntroDataScience_NonTech_S01.Rmd" "session01.R"
Take a look at list.files(getwd())
carefully. We have already learned that getwd()
is an R function. That function takes no input, and returns one output: the path towards the current working directory. Now, list.files(getwd())
composes two R functions: getwd()
is the first one, and list.files()
is the second one. The later takes one input - that being the results of getwd()
- and returns one output, namely the list of files found in the directory on the path returned by a previous getwd()
call. In programming, R represents and instance of something called a functional programming language. Right now, this means very little to you, so we can simply say just the following: you will be using a tons of functions in R and combine them in crazy ways. And that is why it is important to develop an intuition about functions since the very beginning.
WARNING. I can code, but I cannot draw. This is how I envision functions and their composition:
Ok, list.files(getwd())
returned a list of files found under our working directory that we have in turn obtained from getwd()
. There is more: we know that the current working directory is
getwd()
[1] "/Users/goransm/Work/___DataKolektiv/_EDU/DSSVol01_IntroRNonTech2021-22/_Code/IntroDataScience_NonTech_S01"
and now we ask: what is found in C:/Users/goran/___DataKolektiv/__EDU/01_IntroDataScience_Non-Tech/_Code/
, the directory right above our current working directory where we find the .Rmd
R Notebook file (the one that we are now using)?
# THIS IS A COMMENT: This chunk of code makes use of the `list.files()` R function
list.files('/Users/goransm/Work/___DataKolektiv/_EDU/DSSVol01_IntroRNonTech2021-22/_Code/IntroDataScience_NonTech_S01')
[1] "IntroDataScience_NonTech_S01.nb.html" "IntroDataScience_NonTech_S01.Rmd" "session01.R"
Ok, we find one file (e.g. _Code.Rproj
) and many directories (img
, IntroDataScience_NonTech_S00
, IntroDataScience_NonTech_S01
, etc.). Now, imagine I want to work in C:/Users/goran/___DataKolektiv/__EDU/01_IntroDataScience_Non-Tech/_Code/
instead of working in:
getwd()
[1] "/Users/goransm/Work/___DataKolektiv/_EDU/DSSVol01_IntroRNonTech2021-22/_Code/IntroDataScience_NonTech_S01"
How does one change a working directory in R? We have a function (of course) to do that:
setwd('/Users/goransm/Work/___DataKolektiv/_EDU/DSSVol01_IntroRNonTech2021-22/_Code/IntroDataScience_NonTech_S01')
Now, we need to be very careful because we are working from R Markdown: type getwd()
in the console and take a look at its output. Now execute the following chunk of R code here:
getwd()
[1] "/Users/goransm/Work/___DataKolektiv/_EDU/DSSVol01_IntroRNonTech2021-22/_Code/IntroDataScience_NonTech_S01"
So, nothing changed. The moral of the story: changing a directory inside an R Markdown code chunk will change the working directory only until the code in the chunk executes, but it will not change the working directory for any subsequent chunks. If we did this in a console, or in an R script - and both are different from executing the code from an R Markdown Notebook - once called, setwd()
would change the working directory until something else changes it again. That is why R Markdown complained after out setwd()
call.
2. Functions? Variables? Hello, World.
Let’s learn something about the in-built functions R. Those are the R functions - again, think of them simply as pieces of R code that does something - that are placed under your availability as soon as you have installed R. Also, we need to start learning about variables and their corresponding types in R.
You know the famous Hello, World.
message in programming? Typically an exemplar of someone’s first code in a newly discovered programming language… Let’s try:
Hello, World.
Well, that didn’t work. How about:
print('Hello, World.')
[1] "Hello, World."
And here it is. Ok, 'Hello, World.'
, and what about: "Hello, World."
? (Mind the difference, please)
print("Hello, World.")
[1] "Hello, World."
So, strings in R (technically called: characters
) can be encompassed by '
or "
whatsoever: it’s the same. However, It is a good manner of coding practice to choose which one will you use and be consistent in that decision. Note: use "
wherever possible.
Wait, 'Hello, World.'
are two words, right:
word1 <- "Hello"
word2 <- "World"
print(word1)
[1] "Hello"
print(word2)
[1] "World"
Ok, that is not what we wanted, but let’s learn a few things. We have instantiated one variable in R, word1
, and then another one, word2
. By using the R assignment operator - <-
- we have assigned the character value of "Hello"
to word1
and the value of "World"
to word2
. Then we have called the function print()
twice to print out the values of the two variables.
NOTE. You can use =
as an assignment operator in R, of course, try: word = "Hello"
, for example. However, we do not do it by convention in R: we prefer to use the <-
assignment operator. Use =
instead and you will easily confuse any mature R programmer that you eventually need to work with. Don’t do it.
Let’s get closer to the result that we were looking for:
word1 <- "Hello"
word2 <- "World"
paste(word1, word2, sep = ", ")
[1] "Hello, World"
Close. We need an .
at the end, so let’s try:
word1 <- "Hello"
word2 <- "World"
paste(word1, word2, ".", sep = ", ")
[1] "Hello, World, ."
and that is not it, right? Right. We need to start learning something about the logic of R functions like paste()
that puts strings together, obviously. First question, how many arguments did we pass to paste()
in paste(word1, word2, ".", sep = ", ")
?
Answer: four (4) arguments. Let’s see: word1
is the first, word2
the second, now, full stop, .
, is the third, and finally the value for the separator, sep
which we have set to ", "
, is the fourth. paste()
functions by putting together all its first, string arguments, separating them by the value of the sep
argument. But we didn’t need a full stop placed between word1
and word2
! What we do?
We call paste()
twice:
word1 <- "Hello"
word2 <- "World"
paste(
paste(word1, word2, sep = ", "),
"."
)
[1] "Hello, World ."
Almost. What we do not like is the empty space between the end of word2
and .
:
word1 <- "Hello"
word2 <- "World"
paste(
paste(word1, word2, sep = ", "),
".",
sep = ""
)
[1] "Hello, World."
What have we learned: that the sep
argument of paste
has a default value of " "
- an empty space. Only when we have explicitly instructed paste()
to use sep = ""
- an empty string - the results was what we have looked for. As I told you: programming languages are very strict when it comes to communication, they have their inner rules and logic, and one needs to learn and understand that logic completely (or almost completely) to be in full command of a programming language. Also, we have learned that some arguments in some R functions have default values: if we do not specify a value of the argument that we want to use, the function will use its default value.
2. Lists, arrays, and a bit more complicated functions.
What R functions have we seen thus far? Let’s see: print()
, paste()
, list.files()
, getwd()
, setwd()
. There are tons of them, I assure you. Take a look at this one, for example:
word <- 'Hello World'
strsplit(word, split = " ")
[[1]]
[1] "Hello" "World"
Ok, the meaning of strsplit()
is, obviously string split
, and it somehow breaks the strings in pieces, obviously using the split
argument to learn what separator do we want to use to decompose a string, but… But the output looks strange, doesn’t it?
What is [[1]]
, and why does the result follows only after it and an additional [1]
? This is so because strsplit()
returns a list, which is a very important data type in R. Things in R - call them variables - have a data type associated. A thing can be an integer, or a real number, or perhaps a string (again, that data type is called character
in R)… But they can also be a bit more complicated, like arrays and lists. Now, let’s start with lists, very important in R. What is a list?
word <- 'Hello World'
s <- strsplit(word, split = " ")
class(s)
[1] "list"
Yes, I have assigned the output of strsplit(word, split = " ")
to s
, and then used the class()
function to ask for a data type of s
- and it is a list indeed. Let’s print(s)
:
print(s)
[[1]]
[1] "Hello" "World"
Again! But, what if …
print(s[[1]])
[1] "Hello" "World"
Interesting. Here is what is happening:
s
is a list of length one, which means that it has only one element;
- that first element of
s
is found by using the [[1]] index; for lists, the index is always placed in double square brackets: [[
and ]]
, like in: s[[1]]
;
- then there is the
[1] "Hello" "World"
and that is an array - which we can also call a vector - of length two (because it has two elements, Hello
and World
), and [1]
is simply the index of the first element of that array.
This can all sound really complicated at this point, but it is really not. Let’s now start moving one step at the time.
mySequence <- 1:100
print(mySequence)
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
[29] 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
[57] 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
[85] 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
Demystifying the [1]
part of the strsplit()
output - the one for which I’ve explained that is the index of the first element in the array composed of two following elements: "Hello"
and “World
”:
- by
mySequence <- 1:100
I have created an array, a sequence of numbers from 1 to 100, using the :
operator in R; by assignment <-
that sequence becomes the value of the variable mySequence
;
- the
print(mySequence)
line is self-explanatory, and
- from the output we can see that the indices found under square brackets (single! - they are array indices, not list indices) are simply the ordinal numbers of the first element of the
mySequence
array in each row of the ouput - nothing more than that.
That is exactly what happend in the [1] "Hello" "World"
part of the output. Now, about those double square brackets, [[
and ]]
that are used in lists; take a look at this:
myList <- list()
myList[[1]] <- 1:10
myList[[2]] <- 11:20
myList
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10
[[2]]
[1] 11 12 13 14 15 16 17 18 19 20
Does it make more sense now? The myList
list encompasses two arrays, and those two arrays have indices [[1]]
and [[2]]
, of course. So we use [[
and ]]
to index lists in R, and [
and ]
to index arrays. To illustrate, again, with more numbers:
myList <- list()
myList[[1]] <- 1:100
myList[[2]] <- 101:200
myList
[[1]]
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
[29] 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
[57] 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
[85] 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
[[2]]
[1] 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
[29] 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
[57] 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
[85] 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
Do you now understand how list and array indices are used in the R output?
WARNING. My drawing, again: this is how I envision the difference between lists and arrays in R.
Let me try to explain:
- arrays encompass only elements of the same data type: numbers, characters, logicals, etc.;
- I envision arrays as elements that are directly related to each other, “chained” to follow one another;
- lists, on the other hand, I envision as a chain of boxes, and each box can contain anything irrespective of what is found in other boxes;
- I need to unpack a box - i.e. access an element of list by
[[
and ]]
- to find out what is in a box, and I can find various arrays living there;
- Also, something that we will begin to study very soon: a box can live inside a box. In other words: a list can be an element of another list. We call them nested lists in R.
I have used a previously unused function in this Notebook: list()
. There are R functions that are meant to create variables of a certain data type, and list()
is one of them:
a <- list()
a
list()
What is a
?
class(a)
[1] "list"
Of course. What else can I create?
a <- numeric()
a
numeric(0)
Ok, it is a numeric
type of length(0). Now:
a <- numeric(5)
a
[1] 0 0 0 0 0
Now this is an array, or a vector if you like (a and one-dimensional vector, to be precise), of length 5: it has five elements, all 0
valued as per default.
Wait:
a <- character(5)
a
[1] "" "" "" "" ""
Ok, the empty string ""
is the default for creating character arrays with character()
. I’ve been mentioning the word length
for a while. Look:
a <- c(1, 2, 3)
length(a)
[1] 3
Now we understand the R function length()
: tell me how many elements there are in an array. NOTE. The c()
function, of the most commonly used things in R, puts thing together in an array. Nothing else.
But why do we have lists and arrays: both seem to be collections of things that are indexed? Here is the fundamental difference:
- arrays can contain data that all must be of the same type, while
- lists can put up together just anything.
The serious game of R data types begins now.
3. Elementary truths about data types in R
Look:
myArray1 <- c(1,2,3,4)
myArray2 <- c(1,"2",3,4)
print(myArray1)
[1] 1 2 3 4
print(myArray2)
[1] "1" "2" "3" "4"
What? What has just happened? Well, to understand what happened, you need to keep in your mind the fact that R arrays can encompass only elements of the same data type. The first one - myArray1
- is not problematic: it encompasses only things of integer
(or numeric
in R, as you will see) type. But the second one, myArray2
is strange: we produced it by combining three numbers (1
, 2
, and 3
), and one character type ("2"
; never forget, writing out a number to a computer, like 2
, means you want to use a number, while writing out something quoted, like "2"
means you want to use a character, a string, a text - and that is not the same). What did R do? As we can see from the output: [1] "1" "2" "3" "4"
- R has converted everything to a character
type. So, two things to remember: (1) R automatically does type conversion, so for example when it sees you trying to put together numbers and characters, it will default to convert everything to characters, and (2) some data types are “older” than the others, for example character
is obviously “older” than numeric
since R did not try to convert "2"
to a number (as you might have expected) but in turn decided to convert 1
, 2
, and 3
to characters. You will get use to this, don’t worry.
But what if we wanted R to understand that by myArray2 <- c(1,"2",3,4)
we mean numbers? Then:
myArray2 <- as.numeric(c(1,"2",3,4))
print(myArray2)
[1] 1 2 3 4
So, if we want to override R’s implicit, automatic type conversion, which implies that numbers will be automatically converted to characters and not vice versa, we need to instruct R that we want an explicit type conversion by using the appropriate function, as.numeric()
in this case.
For example:
logic <- c(0,1,1,0)
as.logical(logic)
[1] FALSE TRUE TRUE FALSE
What have we learned: there is a data type called logical
in R (also known as Boolean), and it can take only two values: TRUE
and FALSE
(note: always capitalized in R). So, 1
is always TRUE
, while 0
is always FALSE
, right? No:
logic <- c(0,3,1,0)
as.logical(logic)
[1] FALSE TRUE TRUE FALSE
When converting numbers to logicals in R, 0
is always FALSE
. Try as.logical(-4)
, or as.logical(3.14)
to make sure.
Now, what if we want to maintain a collection of data of various types? Then lists come into play:
myList <- list(1, 2, "3", 4)
print(myList)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] "3"
[[4]]
[1] 4
Now:
- the list has four elements:
[[1]]
, [[2]]
, [[3]]
, and [[4]]
;
- each element contains an array of length one:
1
, 2
, "3"
, and 4
, and
- their data types are:
class(myList)
[1] "list"
Oh, no, class()
returned the data type of the whole list! R has functions - a very important and powerful set of functions indeed - to work with lists. Look:
lapply(myList, class)
[[1]]
[1] "numeric"
[[2]]
[1] "numeric"
[[3]]
[1] "character"
[[4]]
[1] "numeric"
lapply()
is an R function that (1) for a given function as its second argument (class()
in this example) (2) returns a result of that function’s call, taking each element of its first argument - which is a list, myList
in our example - as the respective function’s input, and (3) returns a result as a list of the length equal to the length of the input list.
Let’s experiment to understand lapply()
, a function of extreme importance in R programming, better:
myList <- list(1, 2, "3", 4)
lapply(myList, as.numeric)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
So, lapply()
called as.numeric()
on each element in myList
and returned a converted value. Again:
myList <- list(1, 2, "3", 4)
lapply(myList, as.character)
[[1]]
[1] "1"
[[2]]
[1] "2"
[[3]]
[1] "3"
[[4]]
[1] "4"
And again for as.logical
:
myList <- list(1, 2, "3", 4)
lapply(myList, as.logical)
[[1]]
[1] TRUE
[[2]]
[1] TRUE
[[3]]
[1] NA
[[4]]
[1] TRUE
Wait, what is the NA
value in [[3]]
? The third element of the myList
list is a character, "3"
. We asked lapply()
to apply as.logical()
to all members of myList
. It did well for the numbers (all positive, so all TRUE
), but it failed for "3"
which is a character - and R does not know how to treat a character value under as.logical
- should it be TRUE
or FALSE
. Since a conversion of that kind is not defined in R, it returned NA
, short for Not Available
. You will see NA
used a lot in Data Science in R, anytime the value is, for some reason, missing.
4. Our own R functions
Remember how it started: I’ve told you that you should think of R functions simply as collections of R code that does something for us. For example, repetitive things: there is something that can be done with data, but the data can vary while what can be done to it remains the same - and then we need a function. It is a piece of code that always does one and the same thing over it inputs: the things that we pass to it. Say, we pass 3
and 4
to some R function called muFun()
and we want to it to return the result of 3+4
, but we do not want to change it just to be able to do the same for 1
and 2
, or 1000
and 3.14
.
We have seen that R has many in-built functions. When we start adding R packages to our work in the next session you will see that it actually has an unknown, very, very large number of them. But can we define our own functions in R? Of course. It is simple:
myFun <- function(a, b) {
summa <- a + b
return(summa)
}
Nothing happens? We need to call a function from R in order to make it work. Look:
myFun(5, 6)
[1] 11
Let us analyze the function code, line by line:
myFun <- function(a, b)
is the very function definition: it tells R that we want to define a function that takes two input arguments, a
and b
;
{
that follows is it signifies the begging of a code block - a collection of R instructions, new function calls, operations, iterations, etc. that process the function’s arguments a
and b
in some way;
summa <- a + b
: we instantiate a new variable called summa
in the function myFun
and assign it the value of the expression a + b
;
return(summa)
tells the function to stop its execution and sends back the argument of the function return()
to the caller - the R expression that has made the call to myFun
; and finally,
}
closes the code block previously opened with {
.
What happens when we do:
myResult <- myFun(2.5, 2.6)
print(myResult)
[1] 5.1
is the following flow of events:
- the value of
2.5
binds to the function’s a
argument;
- the value of
2.6
binds to the function’s b
argument;
a + b
is executed inside the function and assigned to summa
;
summa
is returned to the caller myResult <- myFun(2.5,2.6)
where the output of myFun
- and that is what return(summa)
defines - binds to myResult;
myResult
is printed by print()
outside the function.
Remember lapply()
? Let’s play a bit:
myList <- list(
'New York, USA',
'Belgrade, Serbia',
'Moscow, Russia',
'Berlin, Germany',
'London, UK',
'Paris, France',
'Rome, Italy'
)
print(myList)
[[1]]
[1] "New York, USA"
[[2]]
[1] "Belgrade, Serbia"
[[3]]
[1] "Moscow, Russia"
[[4]]
[1] "Berlin, Germany"
[[5]]
[1] "London, UK"
[[6]]
[1] "Paris, France"
[[7]]
[1] "Rome, Italy"
Ok, we have a list. I want to write out a function that will return separately the city and the country in which it resides from myList
. Here is a way to do it with our mighty friend, lapply()
:
lapply(myList, strsplit, split = ", ")
[[1]]
[[1]][[1]]
[1] "New York" "USA"
[[2]]
[[2]][[1]]
[1] "Belgrade" "Serbia"
[[3]]
[[3]][[1]]
[1] "Moscow" "Russia"
[[4]]
[[4]][[1]]
[1] "Berlin" "Germany"
[[5]]
[[5]][[1]]
[1] "London" "UK"
[[6]]
[[6]][[1]]
[1] "Paris" "France"
[[7]]
[[7]][[1]]
[1] "Rome" "Italy"
Oh, only that the output looks even more complicated now, what is this:
[[7]]
[[7]][[1]]
[1] "Rome" "Italy"
I know that this now really feels uncomfortable, but I promise you that it is not that difficult at all.
It reads in R in the following way: it is the seventh ([[7]]
) element of the output from lapply
, and that seventh element contains one list ([[7]][[1]]
), and than that list contains an array with the following elements: "Rome"
, and `“Italy”.
Why so complicated? Because of what we have already learned: lapply()
returns a list, but strsplit()
also returns a list, so the result is, naturally, a list of lists.
NOTE. You will need to manage such complicated, nested data structures in R a lot if you are about to enter Data Science. Don’t be scared: it is only practice that you are missing at this point.
Take a careful look to our call to lapply()
again:
lapply(myList, strsplit, split = ", ")
[[1]]
[[1]][[1]]
[1] "New York" "USA"
[[2]]
[[2]][[1]]
[1] "Belgrade" "Serbia"
[[3]]
[[3]][[1]]
[1] "Moscow" "Russia"
[[4]]
[[4]][[1]]
[1] "Berlin" "Germany"
[[5]]
[[5]][[1]]
[1] "London" "UK"
[[6]]
[[6]][[1]]
[1] "Paris" "France"
[[7]]
[[7]][[1]]
[1] "Rome" "Italy"
We have asked R to lapply()
the function strsplit
to myList
, and in the end passed the argument split
which is actually an argument meant for strsplit()
, not lapply()
. This is very handy when you need to lapply()
other functions, but with a specific value for some argument that they might use.
Let’s now try to simplify the result of this operation a bit:
separateCities <- function(string) {
return(
strsplit(string, split = ", ")[[1]]
)
}
lapply(myList, separateCities)
[[1]]
[1] "New York" "USA"
[[2]]
[1] "Belgrade" "Serbia"
[[3]]
[1] "Moscow" "Russia"
[[4]]
[1] "Berlin" "Germany"
[[5]]
[1] "London" "UK"
[[6]]
[1] "Paris" "France"
[[7]]
[1] "Rome" "Italy"
Now that is similar to what we have seen from lapply()
before. How did we achieve this? Let’ analyze our separateCities()
function:
separateCities <- function(string) {
return(
strsplit(string, split = ", ")[[1]]
)
}
In this case, there is no separate “process” and “return output” step: I have nested the processing of the function’s string
input in the return()
call by doing: return(strsplit(string, split = ", ")[[1]])
. I simply found it to be more convenient; for a function with a more complicated code, I would probably avoid doing something likes this. Now, let’s take a look at the strsplit(string, split = ", ")[[1]]
part: what it does is that it calls strsplit()
, passing string
as an input, and defining the value of the split
argument to be ", "
, but I have also added something: [[1]]
at the end of the call to strsplit()
. Why? Because I know that strsplit()
returns a list that will encompass an array with the results that I expect, so that by saying to R something like return(strsplit(x, split = ", ")[[1]])
I am asking for what is inside the list returned by strsplit
, and not the direct result of strsplit(x, split = ", ")
. And what is inside that list is an array. If you still ask yourself where did the output list came from in our lapply()
call: don’t forget that lapply()
returns a list by itself.
Three things and tricks to remember:
lapply()
returns a list; strsplit()
returns a list; and many other R functions as well; if you compose a call to one with a call to another, don’t expect to get anything else back but a list of lists;
- the output of
lapply()
can be simplified by a call to its sister function called sapply()
, like this:
separateCities <- function(string) {
return(
strsplit(string, split = ", ")[[1]]
)
}
sapply(myList, separateCities)
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] "New York" "Belgrade" "Moscow" "Berlin" "London" "Paris" "Rome"
[2,] "USA" "Serbia" "Russia" "Germany" "UK" "France" "Italy"
Now, this looks better somehow (it’s a sort of a tabular structure, the cities are in the first row, the countries in the second row) but we still do not get to understand exactly how sapply()
achieved this. More on data structures like this one will be presented and discussed in our next sessions, especially when we start talking about vectors, matrices, and code vectorization in R.
- Finally, if you want to strip the list of the result of a function that returns a list by its definition, simply pick it up by
[[
and ]]
, as we did in our strsplit()
call: strsplit(string, split = ", ")[[1]]
.
To end this session with an understanding of nested lists in R, because I can imagine many will be confused by that:
a <- list()
a[[1]] <- 5
a[[2]] <- 7
print(a)
[[1]]
[1] 5
[[2]]
[1] 7
Ok, let’ say this is a simple list: it has two elements, each element has one numeric in it. Now:
a <- list()
a[[1]] <- list(5, 7)
a[[2]] <- list(1, 3)
print(a)
[[1]]
[[1]][[1]]
[1] 5
[[1]][[2]]
[1] 7
[[2]]
[[2]][[1]]
[1] 1
[[2]][[2]]
[1] 3
Let’s read it out:
a
has two elements, indexed as [[1]]
(the first one) and [[2]]
(the second one);
- the first element of
a
, which we refer to in R as a[[1]]
, is a list itself, and has two elements, against referenced as [[1]]
and [[2]]
, the first containing a numeric 5
and the second containing a numeric 7
;
- the two lists nested in
a[[1]]
can be acessed by a[[1]][[1]]
and a[[1]][[2]]
respectively:
a[[1]]
[[1]]
[1] 5
[[2]]
[1] 7
a[[1]][[1]]
[1] 5
a[[1]][[2]]
[1] 7
and the same can be done for a[[2]]
:
a[[2]][[1]]
[1] 1
a[[2]][[2]]
[1] 3
Nested lists are not complicated at all, it is just a matter of getting used to the way the R syntax is used to access their elements.
For arrays, we only use [
and ]
to access their elements:
a <- 1:100
a[50]
[1] 50
And, very important, we can slice out elements of arrays:
a[25:50]
[1] 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
Once again, to access an element of a list, use [[
and ]]
:
a <- list(1,2,3,4,5)
a[[4]]
[1] 4
But to slice out a part of the list - a range of its elements, do not use [[
and ]]
, but only [
and ]
:
a <- list(1,2,3,4,5)
a[3:4]
[[1]]
[1] 3
[[2]]
[1] 4
Finally, you will love the fact that things like lapply()
can be used on vectors too:
a <- c(1,2,3,4)
lapply(a, "^", 2)
[[1]]
[1] 1
[[2]]
[1] 4
[[3]]
[1] 9
[[4]]
[1] 16
Did you like it? Here is what I did:
- there is an array
a
,
- then a
lapply()
call to call the function “^” (other languages would call “^” an operator - some reasons why I simply adore R and its functional nature) and pass it a second parameter 2
.
The results is a list of elements in a
all raised on power of 2
.
a[[3]]
[1] 3
a[[3]]^2
[1] 9
Enjoy this:
5^2
[1] 25
"^"(5, 2)
[1] 25
5. data.frame
Lists are not the only beings in R that can hold together things of different types.
Introducing data.frame
, the data type that makes Data Science possible:
# - Comment: the population data represent the most recently updated figures
# - from Wikidata: https://www.wikidata.org/
# - let's produce several vectors:
cities <- c('New York', 'Belgrade', 'Moscow', 'Paris', 'Rome', 'Berlin')
countries <- c('USA', 'Serbia', 'Russia', 'France', 'Italy', 'Germany')
populations <- c(8405837, 1378682, 12692466, 2187526, 2872800, 3644826)
lattitudes <- c(40.730610, 44.787197, 55.751244, 48.864716, 41.902782, 52.520008)
longitudes <- c(-73.935242, 20.457273, 37.618423, 2.349014, 12.496366, 13.404954)
# - now let's have a data.frame:
myCities <- data.frame(city = cities,
country = countries,
population = populations,
lat = lattitudes,
lon = longitudes,
stringsAsFactors = FALSE)
# - display myCities:
print(myCities)
How do we access different columns in a data.frame
? Easy:
myCities$city
[1] "New York" "Belgrade" "Moscow" "Paris" "Rome" "Berlin"
myCities$population
[1] 8405837 1378682 12692466 2187526 2872800 3644826
print(myCities[ , c('lat', 'lon')])
NOTE. Mind the usage of [ ,
in the previous subsetting of the myCities
data.frame
: what if I asked for myCities[1, c('lat', 'lon')]
only:
myCities[1, c('lat', 'lon')]
And what if asked for:
myCities[1:2, c('lat', 'lon')]
But when I say [ , c('lat', 'lon')]
, that means that I want all the rows, and only the c('lat', 'lon')
columns:
myCities[ , c('lat', 'lon')]
We will be learning a lot about data.frames
in our future sessions.
6. The shape of things to come: beauty and simplicity
6.A Example: Interactive geo-visualization in 9 lines of code
Now, allow me to show you how powerful R really is. How many lines of R code do I need to create an interactive map that singles out cities found in the myCities
data.frame
by markers that display information on them when clicked upon? Let’s see:
# - Setup
# - Install {leaflet} if not installed;
# - install.packages('leaflet')
# - {dplyr} should be present already
# - load R packages
library(leaflet)
library(dplyr)
# a single icon is declared
awesome <- makeAwesomeIcon(
icon = "info", iconColor = "black", markerColor = "blue", library = "fa"
)
leaflet(data = myCities) %>%
addTiles() %>%
addAwesomeMarkers(~lon, ~lat, icon = awesome,
popup = ~as.character(population),
label = ~as.character(city))
That would be ten (10) lines of code, not counting the comments of course.
6.B Example: Rapid visualizations w. {ggplot2}
A famous dataset used in Data Science trainings, mtcars
, enters, together with the industrial standard visualization library {ggplot2}
:
# - install {ggplot2} if not installed
# - install.packages('ggplot2')
# - install {ggrepel} if not installed
# - install.packages('ggrepel')
library(ggplot2)
library(ggrepel)
data(mtcars)
mtcars$model <- row.names(mtcars)
mtcars$gear <- as.factor(mtcars$gear)
print(mtcars)
ggplot(data = mtcars,
aes(x = mpg, y = qsec,
label = model,
group = gear,
color = gear)) +
geom_point() +
scale_color_discrete() +
geom_smooth(method = "lm",
size = .15) +
geom_text_repel(size = 3) +
ggtitle("mpg vs. qsec in the mtcars dataset, per gear.") +
theme_bw() +
theme(plot.title = element_text(size = 10)) +
theme(panel.border = element_blank())
`geom_smooth()` using formula 'y ~ x'
But if you want to be sure that you can use this power in a responsible way, you need to get along with vectors, lists, nested lists, functions, code vectorization, and many other things as well.
Stay tuned. We didn’t even start the dance yet.
R Markdown
R Markdown is what I have used to produce this beautiful Notebook. We will learn more about it near the end of the course, but if you already feel ready to dive deep, here’s a book: R Markdown: The Definitive Guide, Yihui Xie, J. J. Allaire, Garrett Grolemunds.
Goran S. Milovanović
DataKolektiv, 2020/21
contact: goran.milovanovic@datakolektiv.com
License: GPLv3 This Notebook is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This Notebook is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this Notebook. If not, see http://www.gnu.org/licenses/.
LS0tCnRpdGxlOiBJbnRybyB0byBEYXRhIFNjaWVuY2UgKE5vbi1UZWNobmljYWwgQmFja2dyb3VuZCwgUikgLSBTZXNzaW9uMDEKYXV0aG9yOgotIG5hbWU6IEdvcmFuIFMuIE1pbG92YW5vdmnEhywgUGhECiAgYWZmaWxpYXRpb246IERhdGFLb2xla3RpdiwgQ2hpZWYgU2NpZW50aXN0ICYgT3duZXI7IERhdGEgU2NpZW50aXN0IGZvciBXaWtpZGF0YSwgV01ERQphYnN0cmFjdDogCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aGVtZTogc3BhY2VsYWIKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKLS0tCgohW10oLi4vX2ltZy9ES19Mb2dvXzEwMC5wbmcpCgoqKioKIyBTZXNzaW9uIDAxOiBGZWVsaW5nIFI6IEJhc2ljcyArIEludHVpdGl2ZSBVbmRlcnN0YW5kaW5nIG9mIFIgCioqRmVlZGJhY2sqKiBzaG91bGQgYmUgc2VuZCB0byBgZ29yYW4ubWlsb3Zhbm92aWNAZGF0YWtvbGVrdGl2LmNvbWAuIApUaGVzZSBub3RlYm9va3MgYWNjb21wYW55IHRoZSBJbnRybyB0byBEYXRhIFNjaWVuY2U6IE5vbi1UZWNobmljYWwgQmFja2dyb3VuZCBjb3Vyc2UgMjAyMC8yMS4KCioqKgoKIyMjIFdlbGNvbWUgdG8gUiEKCiFbXSguLi9faW1nL1Jsb2dvLnBuZykKCiMjIyBXaGF0IGRvIHdlIHdhbnQgdG8gZG8gdG9kYXk/CgpPdXIgZ29hbCBpbiBgU2Vzc2lvbiAwMWAgaXMgdG8gZGV2ZWxvcCBhbmQgaW50dWl0aW9uIG9mIFIsIGFuZCAqYSBmZWVsaW5nIGZvciogdGhlIHByb2dyYW1taW5nIGxhbmd1YWdlIFIuIFllcywgd2UgbmVlZCB0byB3b3JrIG9uIG91ciBlbW90aW9ucywgZGVhciBzdHVkZW50cy4gQmVjYXVzZSBwcm9ncmFtbWluZyBpbiBEYXRhIFNjaWVuY2UgLSBhcyB3ZWxsIGFzIHByb2dyYW1taW5nIGluIGdlbmVyYWwgLSBpcyBub3RoaW5nIGJ1dCBjb21tdW5pY2F0aW9uLCBhbmQgYSB2ZXJ5IHNwZWNpZmljIG9uZS4gUHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIGFyZSBub3QgdmVyeSB0b2xlcmFudCBpbiB0aGUgd2F5IHRoZXkgY29tbXVuaWNhdGUgd2l0aCB1czogZm9yIGV4YW1wbGUsIHRoZXkgZGVtYW5kIHRoYXQgd2UgYmUgdmVyeSB1cGZyb250IGFuZCBzdHJhaWdodGZvcndhcmQsIGFuZCB0aGV5IGhhdmUgbm8gdG9sZXJhbmNlIGZvciBhbWJpZ3VpdHkuIElmIHRoZXkgZG8gbm90IHVuZGVyc3RhbmQgc29tZXRoaW5nIHRoYXQgd2Ugc2F5LCB0aGV5IHdpbGwgaW1tZWRpYXRlbHkgcmVzcG9uZCB3aXRoIGEgY29sZCAtIGFuZCBhdCBmaXJzdCBjcnlwdGljIC0gZXJyb3IgbWVzc2FnZS4gVGhhdCBtZWFucyB0aGF0IHdlIG5lZWQgdG8gYmUgdmVyeSBwYXRpZW50IHdoaWxlIHdlIGxlYXJuIHRoZSBzdHJpY3QgcnVsZXMgb2YgY29tbXVuaWNhdGlvbiB3aXRoIG1hY2hpbmVzIGluIERhdGEgU2NpZW5jZSBhbmQgZWxzZXdoZXJlLiBJIGd1YXJhbnRlZSB5b3UgdGhhdCB0aGVyZSBpcyBhIHNwZWNpZmljIGVtb3Rpb25hbCBzdGF0ZSwgYSBzcGVjaWZpYyBzdGF0ZSBvZiBtaW5kLCB0aGF0IGFjY29tcGFuaWVzIHRoZSBmb2N1c2VkIHdvcmsgb2YgYW55IHByb2dyYW1tZXIgYW5kIERhdGEgU2NpZW50aXN0LiBJdCBpcyBhIGtpbmQgb2YgY2FsbW5lc3MgY291cGxlZCB3aXRoIGEgZ29vZCwgdmVyeSBnb29kIGludHVpdGlvbiBhYm91dCB0aGUgbmF0dXJlIG9mIHRoZSBwcm9ncmFtbWluZyBsYW5ndWFnZSB0aGV5IHVzZS4gVGhpcyBpcyBvdXIgZ29hbDogd2UgbmVlZCB0byBzdGFydCBkZXZlbG9waW5nIG91ciBpbnR1aXRpb24gYWJvdXQgUi4gSG93IGRvZXMgUiBsaWtlIHRvIHdvcms/CgojIyMgMC4gUHJlcmVxdWlzaXRzLgoKTm9uZS4gVGhlcmUgaXMgYW4gUiBjb25zb2xlIG9wZW4gaW4gZnJvbnQgb2YgeW91ciBleWVzIGFuZCB5b3UgY2FuIHJlYWNoIHlvdXIgbWFjaGluZSdzIGtleWJvYXJkLgoKIyMjIDEuIE5hdmlnYXRpbmcgdGhlIHdvcmsgZW52aXJvbm1lbnQ6IHdoZXJlIGFtIEk/CgpFdmVyeXRoaW5nIGhhcHBlbnMgaW4gZm9sZGVycyAob3IgZGlyZWN0b3JpZXMgLSBzaW1wbHkgcGljayB0aGUgdGVybSBvZiB5b3VyIHByZWZlcmVuY2UpLCByaWdodD8gU2F5LCB3ZSB3YW50IHRvIHdyaXRlIG91ciBmaXJzdCBSIHByb2dyYW0gLSBhICpzY3JpcHQqLCBhcyBpdCBpcyB1c3VhbGx5IGNhbGxlZC4gVGhhdCBwcm9ncmFtIHdpbGwgY29uc2lzdCBvZiBhIHNldCBvZiBsaW5lcyBpbiB0aGUgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgUiwgYW5kIHRob3NlIGxpbmVzIHdpbGwgYmUgdGVsbGluZyB0byBvdXIgbWFjaGluZSB3aGF0IHRvIHdlIHdhbnQgZnJvbSBoZXIuIE9rLCB0aGlzIHNldCBvZiBsaW5lcyAtIGEgc2NyaXB0IC0gbmVlZHMgdG8gbGl2ZSBzb21ld2hlcmUuIEl0IG5lZWRzIHRvIGxpdmUgaW4gc29tZSBkaXJlY3Rvcnkgb24geW91ciBjb21wdXRlcidzIGRpc2suIAoKV2hlbiB3ZSB3b3JrIGluIFIsIHRoZXJlIGlzIGFsd2F5cyBzb21ldGhpbmcgY2FsbGVkIGEgKip3b3JraW5nIGRpcmVjdG9yeSoqLiBUaGVyZSBpcyBhICpmdW5jdGlvbiogaW4gUiAtIHRoaW5rIG9mIGl0IGFzIGEgZnJlZSBSIHByb2dyYW0gdGhhdCB5b3UgYWxyZWFkeSBoYXZlIHVwb24gaW5zdGFsbGluZyB0aGlzIHByb2dyYW1taW5nIGxhbmd1YWdlIC0gdGhhdCB3aWxsIGxldCB5b3Uga25vdyB3aGF0IGlzIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4gSWYgeW91IHdyaXRlIGFueSBSIGNvZGUgYW5kIGRlY2lkZSB0byBzYXZlIGl0IGZvciBsYXRlciB1c2UsIGFuZCBkbyBub3Qgc3BlY2lmeSB0aGUgZGlyZWN0b3J5IHdoZXJlIHlvdSB3YW50IGl0IHRvIGxpdmUsIFIgd2lsbCB1c2UgeW91ciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IGFzIGEgZGVzdGluYXRpb24gZm9sZGVyLiBUaGF0IGZ1bmN0aW9uIGlzIGBnZXRfd2QoKWAKCmBgYHtyIGVjaG8gPSBUfQpnZXR3ZCgpCmBgYAoKKipOb3RlLioqIEFzIHlvdSBjYW4gc2VlLCB0aGUgZmlsZSBwYXRocyBpbiBSIGFyZSBhIGJpdCBkaWZmZXJlbnQgdGhhdCB0aG9zZSB0aGF0IHlvdSBhcmUgdXNlZCB0byBzZWUgb24gdGhlIFdpbmRvd3Mgb3BlcmF0aXZlIHN5c3RlbSAoYnV0IGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhvc2Ugd2UgaGF2ZSBvbiBMaW51eCkuIFIgd2lsbCB1c2UgYC9gIHRvIGRlZmluZSBwYXRocywgd2hpbGUgV2luZG93cyB1c2VzIHRoZSBiYWNrc2xhc2ggLSBgXGAgLSBpbnN0ZWFkLiBZb3VyIFJTdHVkaW8gSURFIHRha2VzIGNhcmUgdGhhdCB0aGVzZSBwYXRocyBhcmUgY29tcGF0aWJsZSBzbyB0aGVyZSBpcyBub3RoaW5nIHlvdSBuZWVkIHRvIHdvcnJ5IGFib3V0IChmb3Igbm93KS4KClRoZSBvdXRwdXQgb2YgYGdldHdkKClgIHNob3VsZCBlbmQgd2l0aCBgMDFfSW50cm9EYXRhU2NpZW5jZV9Ob24tVGVjaC9fQ29kZWAgaWYgeW91IGhhdmUgb3BlbmVkIHRoZSBjb3Vyc2UgUlN0dWRpbyBwcm9qZWN0IGV4YWN0bHkgYXMgc3VnZ2VzdGVkIGluIHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBzZXNzaW9uLiBXaGF0IGNvbWVzIGJlZm9yZSBgMDFfSW50cm9EYXRhU2NpZW5jZV9Ob24tVGVjaC9fQ29kZWAgaXMgdGhlIHBhdGggb24geW91ciBsb2NhbCBmaWxlc3lzdGVtIChpLmUuIHlvdXIgbWFjaGluZSkgd2hlcmUgeW91IGhhdmUgY2xvbmVkIHRoZSBjb3Vyc2UgcmVwb3NpdG9yeS4KCk5vdywgbGV0J3MgbGVhcm4gYWJvdXQgdGhlIGNvbnRlbnRzIG9mIG91ciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5OgoKYGBgIHtyIGVjaG8gPSBUfQpsaXN0LmZpbGVzKGdldHdkKCkpCmBgYAoKVGFrZSBhIGxvb2sgYXQgYGxpc3QuZmlsZXMoZ2V0d2QoKSlgIGNhcmVmdWxseS4gV2UgaGF2ZSBhbHJlYWR5IGxlYXJuZWQgdGhhdCBgZ2V0d2QoKWAgaXMgYW4gUiBmdW5jdGlvbi4gVGhhdCBmdW5jdGlvbiB0YWtlcyAqKm5vIGlucHV0KiosIGFuZCByZXR1cm5zIG9uZSAqKm91dHB1dCoqOiB0aGUgcGF0aCB0b3dhcmRzIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LiBOb3csIGBsaXN0LmZpbGVzKGdldHdkKCkpYCAqY29tcG9zZXMqIHR3byBSIGZ1bmN0aW9uczogYGdldHdkKClgIGlzIHRoZSBmaXJzdCBvbmUsIGFuZCBgbGlzdC5maWxlcygpYCBpcyB0aGUgc2Vjb25kIG9uZS4gVGhlIGxhdGVyIHRha2VzICoqb25lIGlucHV0KiogLSB0aGF0IGJlaW5nIHRoZSByZXN1bHRzIG9mIGBnZXR3ZCgpYCAtIGFuZCByZXR1cm5zICoqb25lIG91dHB1dCoqLCBuYW1lbHkgdGhlIGxpc3Qgb2YgZmlsZXMgZm91bmQgaW4gdGhlIGRpcmVjdG9yeSBvbiB0aGUgcGF0aCByZXR1cm5lZCBieSBhIHByZXZpb3VzIGBnZXR3ZCgpYCBjYWxsLiBJbiBwcm9ncmFtbWluZywgUiByZXByZXNlbnRzIGFuZCBpbnN0YW5jZSBvZiBzb21ldGhpbmcgY2FsbGVkIGEgKipmdW5jdGlvbmFsIHByb2dyYW1taW5nIGxhbmd1YWdlKiouIFJpZ2h0IG5vdywgdGhpcyBtZWFucyB2ZXJ5IGxpdHRsZSB0byB5b3UsIHNvIHdlIGNhbiBzaW1wbHkgc2F5IGp1c3QgdGhlIGZvbGxvd2luZzogeW91IHdpbGwgYmUgdXNpbmcgYSB0b25zIG9mIGZ1bmN0aW9ucyBpbiBSIGFuZCBjb21iaW5lIHRoZW0gaW4gY3Jhenkgd2F5cy4gQW5kIHRoYXQgaXMgd2h5IGl0IGlzIGltcG9ydGFudCB0byBkZXZlbG9wIGFuIGludHVpdGlvbiBhYm91dCBmdW5jdGlvbnMgc2luY2UgdGhlIHZlcnkgYmVnaW5uaW5nLgoKKipXQVJOSU5HLioqIEkgY2FuIGNvZGUsIGJ1dCBJIGNhbm5vdCBkcmF3LiBUaGlzIGlzIGhvdyBJIGVudmlzaW9uIGZ1bmN0aW9ucyBhbmQgdGhlaXIgY29tcG9zaXRpb246CgohW10oLi4vX2ltZy9TMDFfMDFfRnVuY3Rpb25zLmpwZWcpCgpPaywgYGxpc3QuZmlsZXMoZ2V0d2QoKSlgIHJldHVybmVkIGEgbGlzdCBvZiAqZmlsZXMqIGZvdW5kIHVuZGVyIG91ciB3b3JraW5nIGRpcmVjdG9yeSB0aGF0IHdlIGhhdmUgaW4gdHVybiBvYnRhaW5lZCBmcm9tIGBnZXR3ZCgpYC4gVGhlcmUgaXMgbW9yZTogd2Uga25vdyB0aGF0IHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IGlzIAoKYGBgIHtyIGVjaG8gPSBUfQpnZXR3ZCgpCmBgYAoKYW5kIG5vdyB3ZSBhc2s6IHdoYXQgaXMgZm91bmQgaW4gYEM6L1VzZXJzL2dvcmFuL19fX0RhdGFLb2xla3Rpdi9fX0VEVS8wMV9JbnRyb0RhdGFTY2llbmNlX05vbi1UZWNoL19Db2RlL2AsIHRoZSBkaXJlY3RvcnkgcmlnaHQgYWJvdmUgb3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3Rvcnkgd2hlcmUgd2UgZmluZCB0aGUgYC5SbWRgIFIgTm90ZWJvb2sgZmlsZSAodGhlIG9uZSB0aGF0IHdlIGFyZSBub3cgdXNpbmcpPwoKYGBgIHtyIGVjaG8gPSBUfQojIFRISVMgSVMgQSBDT01NRU5UOiBUaGlzIGNodW5rIG9mIGNvZGUgbWFrZXMgdXNlIG9mIHRoZSBgbGlzdC5maWxlcygpYCBSIGZ1bmN0aW9uCmxpc3QuZmlsZXMoJy9Vc2Vycy9nb3JhbnNtL1dvcmsvX19fRGF0YUtvbGVrdGl2L19FRFUvRFNTVm9sMDFfSW50cm9STm9uVGVjaDIwMjEtMjIvX0NvZGUvSW50cm9EYXRhU2NpZW5jZV9Ob25UZWNoX1MwMScpCmBgYAoKT2ssIHdlIGZpbmQgb25lIGZpbGUgKGUuZy4gYF9Db2RlLlJwcm9qYCkgYW5kIG1hbnkgZGlyZWN0b3JpZXMgKGBpbWdgLCBgSW50cm9EYXRhU2NpZW5jZV9Ob25UZWNoX1MwMGAsIGBJbnRyb0RhdGFTY2llbmNlX05vblRlY2hfUzAxYCwgZXRjLikuIE5vdywgaW1hZ2luZSBJIHdhbnQgdG8gd29yayBpbiBgQzovVXNlcnMvZ29yYW4vX19fRGF0YUtvbGVrdGl2L19fRURVLzAxX0ludHJvRGF0YVNjaWVuY2VfTm9uLVRlY2gvX0NvZGUvYCBpbnN0ZWFkIG9mIHdvcmtpbmcgaW46CgpgYGAge3IgZWNobyA9IFR9CmdldHdkKCkKYGBgCgpIb3cgZG9lcyBvbmUgY2hhbmdlIGEgd29ya2luZyBkaXJlY3RvcnkgaW4gUj8gV2UgaGF2ZSBhIGZ1bmN0aW9uIChvZiBjb3Vyc2UpIHRvIGRvIHRoYXQ6CgpgYGAge3IgZWNobyA9IFR9CnNldHdkKCcvVXNlcnMvZ29yYW5zbS9Xb3JrL19fX0RhdGFLb2xla3Rpdi9fRURVL0RTU1ZvbDAxX0ludHJvUk5vblRlY2gyMDIxLTIyL19Db2RlL0ludHJvRGF0YVNjaWVuY2VfTm9uVGVjaF9TMDEnKQpgYGAKCk5vdywgd2UgbmVlZCB0byBiZSB2ZXJ5IGNhcmVmdWwgYmVjYXVzZSB3ZSBhcmUgd29ya2luZyBmcm9tIFIgTWFya2Rvd246IHR5cGUgYGdldHdkKClgIGluIHRoZSBjb25zb2xlIGFuZCB0YWtlIGEgbG9vayBhdCBpdHMgb3V0cHV0LiBOb3cgZXhlY3V0ZSB0aGUgZm9sbG93aW5nIGNodW5rIG9mIFIgY29kZSBoZXJlOgoKYGBgIHtyIGVjaG8gPSBUfQpnZXR3ZCgpCmBgYApTbywgbm90aGluZyBjaGFuZ2VkLiBUaGUgbW9yYWwgb2YgdGhlIHN0b3J5OiBjaGFuZ2luZyBhIGRpcmVjdG9yeSAqaW5zaWRlIGFuIFIgTWFya2Rvd24gY29kZSBjaHVuayogd2lsbCBjaGFuZ2UgdGhlIHdvcmtpbmcgZGlyZWN0b3J5IG9ubHkgdW50aWwgdGhlIGNvZGUgaW4gdGhlIGNodW5rIGV4ZWN1dGVzLCBidXQgaXQgd2lsbCBub3QgY2hhbmdlIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBmb3IgYW55IHN1YnNlcXVlbnQgY2h1bmtzLiBJZiB3ZSBkaWQgdGhpcyBpbiBhIGNvbnNvbGUsIG9yIGluIGFuIFIgc2NyaXB0IC0gYW5kIGJvdGggYXJlIGRpZmZlcmVudCBmcm9tIGV4ZWN1dGluZyB0aGUgY29kZSBmcm9tIGFuIFIgTWFya2Rvd24gTm90ZWJvb2sgLSBvbmNlIGNhbGxlZCwgYHNldHdkKClgIHdvdWxkIGNoYW5nZSB0aGUgd29ya2luZyBkaXJlY3RvcnkgdW50aWwgc29tZXRoaW5nIGVsc2UgY2hhbmdlcyBpdCBhZ2Fpbi4gVGhhdCBpcyB3aHkgUiBNYXJrZG93biBjb21wbGFpbmVkIGFmdGVyIG91dCBgc2V0d2QoKWAgY2FsbC4KCgojIyMgMi4gRnVuY3Rpb25zPyBWYXJpYWJsZXM/IEhlbGxvLCBXb3JsZC4KCkxldCdzIGxlYXJuIHNvbWV0aGluZyBhYm91dCB0aGUgaW4tYnVpbHQgZnVuY3Rpb25zIFIuIFRob3NlIGFyZSB0aGUgUiBmdW5jdGlvbnMgLSBhZ2FpbiwgdGhpbmsgb2YgdGhlbSBzaW1wbHkgYXMgcGllY2VzIG9mIFIgY29kZSB0aGF0IGRvZXMgc29tZXRoaW5nIC0gdGhhdCBhcmUgcGxhY2VkIHVuZGVyIHlvdXIgYXZhaWxhYmlsaXR5IGFzIHNvb24gYXMgeW91IGhhdmUgaW5zdGFsbGVkIFIuIEFsc28sIHdlIG5lZWQgdG8gc3RhcnQgbGVhcm5pbmcgYWJvdXQgKnZhcmlhYmxlcyogYW5kIHRoZWlyIGNvcnJlc3BvbmRpbmcgKnR5cGVzKiBpbiBSLgoKWW91IGtub3cgdGhlIGZhbW91cyBgSGVsbG8sIFdvcmxkLmAgbWVzc2FnZSBpbiBwcm9ncmFtbWluZz8gVHlwaWNhbGx5IGFuIGV4ZW1wbGFyIG9mIHNvbWVvbmUncyBmaXJzdCBjb2RlIGluIGEgbmV3bHkgZGlzY292ZXJlZCBwcm9ncmFtbWluZyBsYW5ndWFnZS4uLiBMZXQncyB0cnk6CgpgYGAge3IgZWNobyA9IFQsIGV2YWwgPSBGfQpIZWxsbywgV29ybGQuCmBgYAoKV2VsbCwgdGhhdCBkaWRuJ3Qgd29yay4gSG93IGFib3V0OgoKYGBgIHtyIGVjaG8gPSBUfQpwcmludCgnSGVsbG8sIFdvcmxkLicpCmBgYAoKQW5kIGhlcmUgaXQgaXMuIE9rLCBgJ0hlbGxvLCBXb3JsZC4nYCwgYW5kIHdoYXQgYWJvdXQ6IGAiSGVsbG8sIFdvcmxkLiJgPyAoTWluZCB0aGUgZGlmZmVyZW5jZSwgcGxlYXNlKQoKYGBgIHtyIGVjaG8gPSBUfQpwcmludCgiSGVsbG8sIFdvcmxkLiIpCmBgYAoKU28sICpzdHJpbmdzKiBpbiBSICh0ZWNobmljYWxseSBjYWxsZWQ6IGBjaGFyYWN0ZXJzYCkgY2FuIGJlIGVuY29tcGFzc2VkIGJ5IGAnYCBvciBgImAgd2hhdHNvZXZlcjogaXQncyB0aGUgc2FtZS4gSG93ZXZlciwgSXQgaXMgYSBnb29kIG1hbm5lciBvZiBjb2RpbmcgcHJhY3RpY2UgdG8gY2hvb3NlIHdoaWNoIG9uZSB3aWxsIHlvdSB1c2UgYW5kIGJlIGNvbnNpc3RlbnQgaW4gdGhhdCBkZWNpc2lvbi4gKipOb3RlOioqIHVzZSBgImAgd2hlcmV2ZXIgcG9zc2libGUuCgpXYWl0LCBgJ0hlbGxvLCBXb3JsZC4nYCBhcmUgdHdvIHdvcmRzLCByaWdodDoKCmBgYCB7ciBlY2hvID0gVH0Kd29yZDEgPC0gIkhlbGxvIgp3b3JkMiA8LSAiV29ybGQiCnByaW50KHdvcmQxKQpwcmludCh3b3JkMikKYGBgCk9rLCB0aGF0IGlzIG5vdCB3aGF0IHdlIHdhbnRlZCwgYnV0IGxldCdzIGxlYXJuIGEgZmV3IHRoaW5ncy4gV2UgaGF2ZSBpbnN0YW50aWF0ZWQgb25lIHZhcmlhYmxlIGluIFIsIGB3b3JkMWAsIGFuZCB0aGVuIGFub3RoZXIgb25lLCBgd29yZDJgLiBCeSB1c2luZyB0aGUgUiAqYXNzaWdubWVudCBvcGVyYXRvciogLSBgPC1gIC0gd2UgaGF2ZSBhc3NpZ25lZCB0aGUgY2hhcmFjdGVyIHZhbHVlIG9mIGAiSGVsbG8iYCB0byBgd29yZDFgIGFuZCB0aGUgdmFsdWUgb2YgYCJXb3JsZCJgIHRvIGB3b3JkMmAuIFRoZW4gd2UgaGF2ZSBjYWxsZWQgdGhlIGZ1bmN0aW9uIGBwcmludCgpYCB0d2ljZSB0byBwcmludCBvdXQgdGhlIHZhbHVlcyBvZiB0aGUgdHdvIHZhcmlhYmxlcy4KCioqTk9URS4qKiBZb3UgY2FuIHVzZSBgPWAgYXMgYW4gYXNzaWdubWVudCBvcGVyYXRvciBpbiBSLCBvZiBjb3Vyc2UsIHRyeTogYHdvcmQgPSAiSGVsbG8iYCwgZm9yIGV4YW1wbGUuIEhvd2V2ZXIsICoqd2UgZG8gbm90IGRvIGl0IGJ5IGNvbnZlbnRpb24gaW4gUjogd2UgcHJlZmVyIHRvIHVzZSB0aGUgYDwtYCBhc3NpZ25tZW50IG9wZXJhdG9yKiouIFVzZSBgPWAgaW5zdGVhZCBhbmQgeW91IHdpbGwgZWFzaWx5IGNvbmZ1c2UgYW55IG1hdHVyZSBSIHByb2dyYW1tZXIgdGhhdCB5b3UgZXZlbnR1YWxseSBuZWVkIHRvIHdvcmsgd2l0aC4gRG9uJ3QgZG8gaXQuCgpMZXQncyBnZXQgY2xvc2VyIHRvIHRoZSByZXN1bHQgdGhhdCB3ZSB3ZXJlIGxvb2tpbmcgZm9yOgoKYGBgIHtyIGVjaG8gPSBUfQp3b3JkMSA8LSAiSGVsbG8iCndvcmQyIDwtICJXb3JsZCIKcGFzdGUod29yZDEsIHdvcmQyLCBzZXAgPSAiLCAiKQpgYGAKCkNsb3NlLiBXZSBuZWVkIGFuIGAuYCBhdCB0aGUgZW5kLCBzbyBsZXQncyB0cnk6CgpgYGAge3IgZWNobyA9IFR9CndvcmQxIDwtICJIZWxsbyIKd29yZDIgPC0gIldvcmxkIgpwYXN0ZSh3b3JkMSwgd29yZDIsICIuIiwgc2VwID0gIiwgIikKYGBgCgphbmQgdGhhdCBpcyBub3QgaXQsIHJpZ2h0PyBSaWdodC4gV2UgbmVlZCB0byBzdGFydCBsZWFybmluZyBzb21ldGhpbmcgYWJvdXQgdGhlIGxvZ2ljIG9mIFIgZnVuY3Rpb25zIGxpa2UgYHBhc3RlKClgIHRoYXQgcHV0cyBzdHJpbmdzIHRvZ2V0aGVyLCBvYnZpb3VzbHkuIEZpcnN0IHF1ZXN0aW9uLCBob3cgbWFueSAqYXJndW1lbnRzKiBkaWQgd2UgcGFzcyB0byBgcGFzdGUoKWAgaW4gYHBhc3RlKHdvcmQxLCB3b3JkMiwgIi4iLCBzZXAgPSAiLCAiKWA/CgpBbnN3ZXI6IGZvdXIgKDQpIGFyZ3VtZW50cy4gTGV0J3Mgc2VlOiBgd29yZDFgIGlzIHRoZSBmaXJzdCwgYHdvcmQyYCB0aGUgc2Vjb25kLCBub3csIGZ1bGwgc3RvcCwgYC5gLCBpcyB0aGUgdGhpcmQsIGFuZCBmaW5hbGx5IHRoZSB2YWx1ZSBmb3IgdGhlIHNlcGFyYXRvciwgYHNlcGAgd2hpY2ggd2UgaGF2ZSBzZXQgdG8gYCIsICJgLCBpcyB0aGUgZm91cnRoLiBgcGFzdGUoKWAgZnVuY3Rpb25zIGJ5IHB1dHRpbmcgdG9nZXRoZXIgYWxsIGl0cyBmaXJzdCwgc3RyaW5nIGFyZ3VtZW50cywgc2VwYXJhdGluZyB0aGVtIGJ5IHRoZSB2YWx1ZSBvZiB0aGUgYHNlcGAgYXJndW1lbnQuIEJ1dCB3ZSBkaWRuJ3QgbmVlZCBhIGZ1bGwgc3RvcCBwbGFjZWQgYmV0d2VlbiBgd29yZDFgIGFuZCBgd29yZDJgISBXaGF0IHdlIGRvPyAKCldlIGNhbGwgYHBhc3RlKClgIHR3aWNlOgoKYGBgIHtyIGVjaG8gPSBUfQp3b3JkMSA8LSAiSGVsbG8iCndvcmQyIDwtICJXb3JsZCIKcGFzdGUoCiAgcGFzdGUod29yZDEsIHdvcmQyLCBzZXAgPSAiLCAiKSwKICAiLiIKKQpgYGAKCkFsbW9zdC4gV2hhdCB3ZSBkbyBub3QgbGlrZSBpcyB0aGUgZW1wdHkgc3BhY2UgYmV0d2VlbiB0aGUgZW5kIG9mIGB3b3JkMmAgYW5kIGAuYDoKCmBgYCB7ciBlY2hvID0gVH0Kd29yZDEgPC0gIkhlbGxvIgp3b3JkMiA8LSAiV29ybGQiCnBhc3RlKAogIHBhc3RlKHdvcmQxLCB3b3JkMiwgc2VwID0gIiwgIiksCiAgIi4iLCAKICBzZXAgPSAiIgopCmBgYAoKV2hhdCBoYXZlIHdlIGxlYXJuZWQ6IHRoYXQgdGhlIGBzZXBgIGFyZ3VtZW50IG9mIGBwYXN0ZWAgaGFzIGEgKmRlZmF1bHQqIHZhbHVlIG9mIGAiICJgIC0gYW4gZW1wdHkgc3BhY2UuIE9ubHkgd2hlbiB3ZSBoYXZlIGV4cGxpY2l0bHkgaW5zdHJ1Y3RlZCBgcGFzdGUoKWAgdG8gdXNlIGBzZXAgPSAiImAgLSBhbiBlbXB0eSBzdHJpbmcgLSB0aGUgcmVzdWx0cyB3YXMgd2hhdCB3ZSBoYXZlIGxvb2tlZCBmb3IuIEFzIEkgdG9sZCB5b3U6IHByb2dyYW1taW5nIGxhbmd1YWdlcyBhcmUgdmVyeSBzdHJpY3Qgd2hlbiBpdCBjb21lcyB0byBjb21tdW5pY2F0aW9uLCB0aGV5IGhhdmUgdGhlaXIgaW5uZXIgcnVsZXMgYW5kIGxvZ2ljLCBhbmQgb25lIG5lZWRzIHRvIGxlYXJuIGFuZCB1bmRlcnN0YW5kIHRoYXQgbG9naWMgY29tcGxldGVseSAob3IgYWxtb3N0IGNvbXBsZXRlbHkpIHRvIGJlIGluIGZ1bGwgY29tbWFuZCBvZiBhIHByb2dyYW1taW5nIGxhbmd1YWdlLiBBbHNvLCB3ZSBoYXZlIGxlYXJuZWQgdGhhdCBzb21lIGFyZ3VtZW50cyBpbiBzb21lIFIgZnVuY3Rpb25zIGhhdmUgKmRlZmF1bHQgdmFsdWVzKjogaWYgd2UgZG8gbm90IHNwZWNpZnkgYSB2YWx1ZSBvZiB0aGUgYXJndW1lbnQgdGhhdCB3ZSB3YW50IHRvIHVzZSwgdGhlIGZ1bmN0aW9uIHdpbGwgdXNlIGl0cyBkZWZhdWx0IHZhbHVlLiAKCiMjIyAyLiBMaXN0cywgYXJyYXlzLCBhbmQgYSBiaXQgbW9yZSBjb21wbGljYXRlZCBmdW5jdGlvbnMuCgoKV2hhdCBSIGZ1bmN0aW9ucyBoYXZlIHdlIHNlZW4gdGh1cyBmYXI/IExldCdzIHNlZTogYHByaW50KClgLCBgcGFzdGUoKWAsIGBsaXN0LmZpbGVzKClgLCBgZ2V0d2QoKWAsIGBzZXR3ZCgpYC4gVGhlcmUgYXJlIHRvbnMgb2YgdGhlbSwgSSBhc3N1cmUgeW91LiBUYWtlIGEgbG9vayBhdCB0aGlzIG9uZSwgZm9yIGV4YW1wbGU6CgpgYGAge3IgZWNobyA9IFR9CndvcmQgPC0gJ0hlbGxvIFdvcmxkJwpzdHJzcGxpdCh3b3JkLCBzcGxpdCA9ICIgIikKYGBgCk9rLCB0aGUgbWVhbmluZyBvZiBgc3Ryc3BsaXQoKWAgaXMsIG9idmlvdXNseSBgc3RyaW5nIHNwbGl0YCwgYW5kIGl0IHNvbWVob3cgYnJlYWtzIHRoZSBzdHJpbmdzIGluIHBpZWNlcywgb2J2aW91c2x5IHVzaW5nIHRoZSBgc3BsaXRgIGFyZ3VtZW50IHRvIGxlYXJuIHdoYXQgc2VwYXJhdG9yIGRvIHdlIHdhbnQgdG8gdXNlIHRvIGRlY29tcG9zZSBhIHN0cmluZywgYnV0Li4uIEJ1dCB0aGUgb3V0cHV0IGxvb2tzIHN0cmFuZ2UsIGRvZXNuJ3QgaXQ/CgpXaGF0IGlzIGBbWzFdXWAsIGFuZCB3aHkgZG9lcyB0aGUgcmVzdWx0IGZvbGxvd3Mgb25seSBhZnRlciBpdCBhbmQgYW4gYWRkaXRpb25hbCBgWzFdYD8gVGhpcyBpcyBzbyBiZWNhdXNlIGBzdHJzcGxpdCgpYCByZXR1cm5zIGEgKipsaXN0KiosIHdoaWNoIGlzIGEgdmVyeSBpbXBvcnRhbnQgZGF0YSB0eXBlIGluIFIuIFRoaW5ncyBpbiBSIC0gY2FsbCB0aGVtIHZhcmlhYmxlcyAtIGhhdmUgYSBkYXRhIHR5cGUgYXNzb2NpYXRlZC4gQSB0aGluZyBjYW4gYmUgYW4gaW50ZWdlciwgb3IgYSByZWFsIG51bWJlciwgb3IgcGVyaGFwcyBhIHN0cmluZyAoYWdhaW4sIHRoYXQgZGF0YSB0eXBlIGlzIGNhbGxlZCBgY2hhcmFjdGVyYCBpbiBSKS4uLiBCdXQgdGhleSBjYW4gYWxzbyBiZSBhIGJpdCBtb3JlIGNvbXBsaWNhdGVkLCBsaWtlICphcnJheXMqIGFuZCAqbGlzdHMqLiBOb3csIGxldCdzIHN0YXJ0IHdpdGggbGlzdHMsIHZlcnkgaW1wb3J0YW50IGluIFIuIFdoYXQgaXMgYSBsaXN0PwoKYGBgIHtyIGVjaG8gPSBUfQp3b3JkIDwtICdIZWxsbyBXb3JsZCcKcyA8LSBzdHJzcGxpdCh3b3JkLCBzcGxpdCA9ICIgIikKY2xhc3MocykKYGBgClllcywgSSBoYXZlIGFzc2lnbmVkIHRoZSBvdXRwdXQgb2YgYHN0cnNwbGl0KHdvcmQsIHNwbGl0ID0gIiAiKWAgdG8gYHNgLCBhbmQgdGhlbiB1c2VkIHRoZSBgY2xhc3MoKWAgZnVuY3Rpb24gdG8gYXNrIGZvciBhIGRhdGEgdHlwZSBvZiBgc2AgLSBhbmQgaXQgaXMgYSBsaXN0IGluZGVlZC4gTGV0J3MgYHByaW50KHMpYDoKCmBgYCB7ciBlY2hvID0gVH0KcHJpbnQocykKYGBgCgpBZ2FpbiEgQnV0LCB3aGF0IGlmIC4uLgoKYGBgIHtyIGVjaG8gPSBUfQpwcmludChzW1sxXV0pCmBgYAoKSW50ZXJlc3RpbmcuIEhlcmUgaXMgd2hhdCBpcyBoYXBwZW5pbmc6CgotIGBzYCBpcyBhIGxpc3Qgb2YgbGVuZ3RoIG9uZSwgd2hpY2ggbWVhbnMgdGhhdCBpdCBoYXMgb25seSBvbmUgZWxlbWVudDsKLSB0aGF0IGZpcnN0IGVsZW1lbnQgb2YgYHNgIGlzIGZvdW5kIGJ5IHVzaW5nIHRoZSBbWzFdXSBpbmRleDsgZm9yIGxpc3RzLCB0aGUgaW5kZXggaXMgYWx3YXlzIHBsYWNlZCBpbiAqKmRvdWJsZSBzcXVhcmUgYnJhY2tldHMqKjogYFtbYCBhbmQgYF1dYCwgbGlrZSBpbjogYHNbWzFdXWA7Ci0gdGhlbiB0aGVyZSBpcyB0aGUgYFsxXSAiSGVsbG8iICJXb3JsZCJgIGFuZCB0aGF0IGlzIGFuICoqYXJyYXkqKiAtIHdoaWNoIHdlIGNhbiBhbHNvIGNhbGwgYSAqKnZlY3RvcioqIC0gb2YgbGVuZ3RoIHR3byAoYmVjYXVzZSBpdCBoYXMgdHdvIGVsZW1lbnRzLCBgSGVsbG9gIGFuZCBgV29ybGRgKSwgYW5kIGBbMV1gIGlzIHNpbXBseSB0aGUgaW5kZXggb2YgdGhlIGZpcnN0IGVsZW1lbnQgb2YgdGhhdCBhcnJheS4KClRoaXMgY2FuIGFsbCBzb3VuZCByZWFsbHkgY29tcGxpY2F0ZWQgYXQgdGhpcyBwb2ludCwgYnV0IGl0IGlzIHJlYWxseSBub3QuIExldCdzIG5vdyBzdGFydCBtb3Zpbmcgb25lIHN0ZXAgYXQgdGhlIHRpbWUuCgpgYGAge3IgZWNobyA9IFR9Cm15U2VxdWVuY2UgPC0gMToxMDAKcHJpbnQobXlTZXF1ZW5jZSkKYGBgCgpEZW15c3RpZnlpbmcgdGhlIGBbMV1gIHBhcnQgb2YgdGhlIGBzdHJzcGxpdCgpYCBvdXRwdXQgLSB0aGUgb25lIGZvciB3aGljaCBJJ3ZlIGV4cGxhaW5lZCB0aGF0IGlzIHRoZSBpbmRleCBvZiB0aGUgZmlyc3QgZWxlbWVudCBpbiB0aGUgYXJyYXkgY29tcG9zZWQgb2YgdHdvIGZvbGxvd2luZyBlbGVtZW50czogYCJIZWxsbyJgIGFuZCAiYFdvcmxkYCI6CgotIGJ5IGBteVNlcXVlbmNlIDwtIDE6MTAwYCBJIGhhdmUgY3JlYXRlZCBhbiBhcnJheSwgYSBzZXF1ZW5jZSBvZiBudW1iZXJzIGZyb20gMSB0byAxMDAsIHVzaW5nIHRoZSBgOmAgb3BlcmF0b3IgaW4gUjsgYnkgYXNzaWdubWVudCBgPC1gIHRoYXQgc2VxdWVuY2UgYmVjb21lcyB0aGUgdmFsdWUgb2YgdGhlIHZhcmlhYmxlIGBteVNlcXVlbmNlYDsKLSB0aGUgYHByaW50KG15U2VxdWVuY2UpYCBsaW5lIGlzIHNlbGYtZXhwbGFuYXRvcnksIGFuZAotIGZyb20gdGhlIG91dHB1dCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGluZGljZXMgZm91bmQgdW5kZXIgc3F1YXJlIGJyYWNrZXRzIChzaW5nbGUhIC0gdGhleSBhcmUgYXJyYXkgaW5kaWNlcywgbm90IGxpc3QgaW5kaWNlcykgYXJlIHNpbXBseSB0aGUgb3JkaW5hbCBudW1iZXJzIG9mIHRoZSBmaXJzdCBlbGVtZW50IG9mIHRoZSBgbXlTZXF1ZW5jZWAgYXJyYXkgaW4gZWFjaCByb3cgb2YgdGhlIG91cHV0IC0gbm90aGluZyBtb3JlIHRoYW4gdGhhdC4gCgpUaGF0IGlzIGV4YWN0bHkgd2hhdCBoYXBwZW5kIGluIHRoZSBgWzFdICJIZWxsbyIgIldvcmxkImAgcGFydCBvZiB0aGUgb3V0cHV0LiBOb3csIGFib3V0IHRob3NlIGRvdWJsZSBzcXVhcmUgYnJhY2tldHMsIGBbW2AgYW5kIGBdXWAgdGhhdCBhcmUgdXNlZCBpbiBsaXN0czsgdGFrZSBhIGxvb2sgYXQgdGhpczoKCmBgYCB7ciBlY2hvID0gVH0KbXlMaXN0IDwtIGxpc3QoKQpteUxpc3RbWzFdXSA8LSAxOjEwCm15TGlzdFtbMl1dIDwtIDExOjIwCm15TGlzdApgYGAKCkRvZXMgaXQgbWFrZSBtb3JlIHNlbnNlIG5vdz8gVGhlIGBteUxpc3RgIGxpc3QgZW5jb21wYXNzZXMgdHdvIGFycmF5cywgYW5kIHRob3NlIHR3byBhcnJheXMgaGF2ZSBpbmRpY2VzIGBbWzFdXWAgYW5kIGBbWzJdXWAsIG9mIGNvdXJzZS4gU28gd2UgdXNlIGBbW2AgYW5kIGBdXWAgdG8gaW5kZXggbGlzdHMgaW4gUiwgYW5kIGBbYCBhbmQgYF1gIHRvIGluZGV4IGFycmF5cy4gVG8gaWxsdXN0cmF0ZSwgYWdhaW4sIHdpdGggbW9yZSBudW1iZXJzOgoKYGBgIHtyIGVjaG8gPSBUfQpteUxpc3QgPC0gbGlzdCgpCm15TGlzdFtbMV1dIDwtIDE6MTAwCm15TGlzdFtbMl1dIDwtIDEwMToyMDAKbXlMaXN0CmBgYAoKRG8geW91IG5vdyB1bmRlcnN0YW5kIGhvdyBsaXN0IGFuZCBhcnJheSBpbmRpY2VzIGFyZSB1c2VkIGluIHRoZSBSIG91dHB1dD8KCioqV0FSTklORy4qKiBNeSBkcmF3aW5nLCBhZ2FpbjogdGhpcyBpcyBob3cgSSBlbnZpc2lvbiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGxpc3RzIGFuZCBhcnJheXMgaW4gUi4KCiFbXSguLi9faW1nL1MwMV8wMl9BcnJheXNfYW5kX0xpc3RzLmpwZWcpCgpMZXQgbWUgdHJ5IHRvIGV4cGxhaW46CgotIGFycmF5cyBlbmNvbXBhc3Mgb25seSBlbGVtZW50cyBvZiB0aGUgc2FtZSBkYXRhIHR5cGU6IG51bWJlcnMsIGNoYXJhY3RlcnMsIGxvZ2ljYWxzLCBldGMuOwotIEkgZW52aXNpb24gYXJyYXlzIGFzIGVsZW1lbnRzIHRoYXQgYXJlIGRpcmVjdGx5IHJlbGF0ZWQgdG8gZWFjaCBvdGhlciwgImNoYWluZWQiIHRvIGZvbGxvdyBvbmUgYW5vdGhlcjsKLSBsaXN0cywgb24gdGhlIG90aGVyIGhhbmQsIEkgZW52aXNpb24gYXMgYSBjaGFpbiBvZiBib3hlcywgYW5kIGVhY2ggYm94IGNhbiBjb250YWluIGFueXRoaW5nIGlycmVzcGVjdGl2ZSBvZiB3aGF0IGlzIGZvdW5kIGluIG90aGVyIGJveGVzOwotIEkgbmVlZCB0byB1bnBhY2sgYSBib3ggLSBpLmUuIGFjY2VzcyBhbiBlbGVtZW50IG9mIGxpc3QgYnkgYFtbYCBhbmQgYF1dYCAtIHRvIGZpbmQgb3V0IHdoYXQgaXMgaW4gYSBib3gsIGFuZCBJIGNhbiBmaW5kIHZhcmlvdXMgYXJyYXlzIGxpdmluZyB0aGVyZTsKLSBBbHNvLCBzb21ldGhpbmcgdGhhdCB3ZSB3aWxsIGJlZ2luIHRvIHN0dWR5IHZlcnkgc29vbjogYSBib3ggY2FuIGxpdmUgaW5zaWRlIGEgYm94LiBJbiBvdGhlciB3b3JkczogYSBsaXN0IGNhbiBiZSBhbiBlbGVtZW50IG9mIGFub3RoZXIgbGlzdC4gV2UgY2FsbCB0aGVtICpuZXN0ZWQgbGlzdHMqIGluIFIuCgpJIGhhdmUgdXNlZCBhIHByZXZpb3VzbHkgdW51c2VkIGZ1bmN0aW9uIGluIHRoaXMgTm90ZWJvb2s6IGBsaXN0KClgLiBUaGVyZSBhcmUgUiBmdW5jdGlvbnMgdGhhdCBhcmUgbWVhbnQgdG8gY3JlYXRlIHZhcmlhYmxlcyBvZiBhIGNlcnRhaW4gZGF0YSB0eXBlLCBhbmQgYGxpc3QoKWAgaXMgb25lIG9mIHRoZW06CgpgYGAge3IgZWNobyA9IFR9CmEgPC0gbGlzdCgpCmEKYGBgCgpXaGF0IGlzIGBhYD8KCmBgYCB7ciBlY2hvID0gVH0KY2xhc3MoYSkKYGBgCgpPZiBjb3Vyc2UuIFdoYXQgZWxzZSBjYW4gSSBjcmVhdGU/CgpgYGAge3IgZWNobyA9IFR9CmEgPC0gbnVtZXJpYygpCmEKYGBgCk9rLCBpdCBpcyBhIGBudW1lcmljYCB0eXBlIG9mIGxlbmd0aCgwKS4gTm93OgoKYGBgIHtyIGVjaG8gPSBUfQphIDwtIG51bWVyaWMoNSkKYQpgYGAKCk5vdyB0aGlzIGlzIGFuICphcnJheSosIG9yIGEgKnZlY3RvciogaWYgeW91IGxpa2UgKGEgYW5kIG9uZS1kaW1lbnNpb25hbCB2ZWN0b3IsIHRvIGJlIHByZWNpc2UpLCBvZiBsZW5ndGggNTogaXQgaGFzIGZpdmUgZWxlbWVudHMsIGFsbCBgMGAgdmFsdWVkIGFzIHBlciBkZWZhdWx0LgoKV2FpdDoKCmBgYCB7ciBlY2hvID0gVH0KYSA8LSBjaGFyYWN0ZXIoNSkKYQpgYGAKT2ssIHRoZSBlbXB0eSBzdHJpbmcgYCIiYCBpcyB0aGUgZGVmYXVsdCBmb3IgY3JlYXRpbmcgY2hhcmFjdGVyIGFycmF5cyB3aXRoIGBjaGFyYWN0ZXIoKWAuIEkndmUgYmVlbiBtZW50aW9uaW5nIHRoZSB3b3JkIGBsZW5ndGhgIGZvciBhIHdoaWxlLiBMb29rOgoKYGBgIHtyIGVjaG89IFR9CmEgPC0gYygxLCAyLCAzKQpsZW5ndGgoYSkKYGBgCk5vdyB3ZSB1bmRlcnN0YW5kIHRoZSBSIGZ1bmN0aW9uIGBsZW5ndGgoKWA6IHRlbGwgbWUgaG93IG1hbnkgZWxlbWVudHMgdGhlcmUgYXJlIGluIGFuIGFycmF5LgoqKk5PVEUuKiogVGhlIGBjKClgIGZ1bmN0aW9uLCBvZiB0aGUgbW9zdCBjb21tb25seSB1c2VkIHRoaW5ncyBpbiBSLCBwdXRzIHRoaW5nIHRvZ2V0aGVyIGluIGFuIGFycmF5LiBOb3RoaW5nIGVsc2UuIAoKQnV0ICoqd2h5KiogZG8gd2UgaGF2ZSBsaXN0cyBhbmQgYXJyYXlzOiBib3RoIHNlZW0gdG8gYmUgY29sbGVjdGlvbnMgb2YgdGhpbmdzIHRoYXQgYXJlIGluZGV4ZWQ/IEhlcmUgaXMgdGhlIGZ1bmRhbWVudGFsIGRpZmZlcmVuY2U6CgotICphcnJheXMqIGNhbiBjb250YWluIGRhdGEgdGhhdCBhbGwgbXVzdCBiZSBvZiB0aGUgKipzYW1lKiogdHlwZSwgd2hpbGUKLSAqbGlzdHMqIGNhbiBwdXQgdXAgdG9nZXRoZXIganVzdCAqKmFueXRoaW5nKiouCgpUaGUgc2VyaW91cyBnYW1lIG9mIFIgZGF0YSB0eXBlcyBiZWdpbnMgbm93LiAKCiMjIyAzLiBFbGVtZW50YXJ5IHRydXRocyBhYm91dCBkYXRhIHR5cGVzIGluIFIKCkxvb2s6CgpgYGAge3IgZWNobyA9IFR9Cm15QXJyYXkxIDwtIGMoMSwyLDMsNCkKbXlBcnJheTIgPC0gYygxLCIyIiwzLDQpCnByaW50KG15QXJyYXkxKQpwcmludChteUFycmF5MikKYGBgCgpXaGF0PyBXaGF0IGhhcyBqdXN0IGhhcHBlbmVkPyBXZWxsLCB0byB1bmRlcnN0YW5kIHdoYXQgaGFwcGVuZWQsIHlvdSBuZWVkIHRvIGtlZXAgaW4geW91ciBtaW5kIHRoZSBmYWN0IHRoYXQgUiBhcnJheXMgY2FuIGVuY29tcGFzcyBvbmx5IGVsZW1lbnRzIG9mIHRoZSBzYW1lIGRhdGEgdHlwZS4gVGhlIGZpcnN0IG9uZSAtIGBteUFycmF5MWAgLSBpcyBub3QgcHJvYmxlbWF0aWM6IGl0IGVuY29tcGFzc2VzIG9ubHkgdGhpbmdzIG9mIGBpbnRlZ2VyYCAob3IgYG51bWVyaWNgIGluIFIsIGFzIHlvdSB3aWxsIHNlZSkgdHlwZS4gQnV0IHRoZSBzZWNvbmQgb25lLCBgbXlBcnJheTJgIGlzIHN0cmFuZ2U6IHdlIHByb2R1Y2VkIGl0IGJ5IGNvbWJpbmluZyB0aHJlZSBudW1iZXJzIChgMWAsIGAyYCwgYW5kIGAzYCksIGFuZCBvbmUgKmNoYXJhY3RlciogdHlwZSAoYCIyImA7IG5ldmVyIGZvcmdldCwgd3JpdGluZyBvdXQgYSBudW1iZXIgdG8gYSBjb21wdXRlciwgbGlrZSBgMmAsIG1lYW5zIHlvdSB3YW50IHRvIHVzZSBhIG51bWJlciwgd2hpbGUgd3JpdGluZyBvdXQgc29tZXRoaW5nIHF1b3RlZCwgbGlrZSBgIjIiYCBtZWFucyB5b3Ugd2FudCB0byB1c2UgYSBjaGFyYWN0ZXIsIGEgc3RyaW5nLCBhIHRleHQgLSBhbmQgdGhhdCBpcyBub3QgdGhlIHNhbWUpLiBXaGF0IGRpZCBSIGRvPyBBcyB3ZSBjYW4gc2VlIGZyb20gdGhlIG91dHB1dDogYFsxXSAiMSIgIjIiICIzIiAiNCJgIC0gUiBoYXMgKipjb252ZXJ0ZWQqKiBldmVyeXRoaW5nIHRvIGEgYGNoYXJhY3RlcmAgdHlwZS4gU28sIHR3byB0aGluZ3MgdG8gcmVtZW1iZXI6ICgxKSBSIGF1dG9tYXRpY2FsbHkgZG9lcyB0eXBlIGNvbnZlcnNpb24sIHNvIGZvciBleGFtcGxlIHdoZW4gaXQgc2VlcyB5b3UgdHJ5aW5nIHRvIHB1dCB0b2dldGhlciBudW1iZXJzIGFuZCBjaGFyYWN0ZXJzLCBpdCB3aWxsIGRlZmF1bHQgdG8gY29udmVydCBldmVyeXRoaW5nIHRvIGNoYXJhY3RlcnMsIGFuZCAoMikgc29tZSBkYXRhIHR5cGVzIGFyZSAib2xkZXIiIHRoYW4gdGhlIG90aGVycywgZm9yIGV4YW1wbGUgYGNoYXJhY3RlcmAgaXMgb2J2aW91c2x5ICJvbGRlciIgdGhhbiBgbnVtZXJpY2Agc2luY2UgUiBkaWQgbm90IHRyeSB0byBjb252ZXJ0IGAiMiJgIHRvIGEgbnVtYmVyIChhcyB5b3UgbWlnaHQgaGF2ZSBleHBlY3RlZCkgYnV0IGluIHR1cm4gZGVjaWRlZCB0byBjb252ZXJ0IGAxYCwgYDJgLCBhbmQgYDNgIHRvIGNoYXJhY3RlcnMuIFlvdSB3aWxsIGdldCB1c2UgdG8gdGhpcywgZG9uJ3Qgd29ycnkuCgpCdXQgd2hhdCBpZiB3ZSB3YW50ZWQgUiB0byB1bmRlcnN0YW5kIHRoYXQgYnkgYG15QXJyYXkyIDwtIGMoMSwiMiIsMyw0KWAgd2UgbWVhbiBudW1iZXJzPyBUaGVuOgoKYGBgIHtyIGVjaG8gPSBUfQpteUFycmF5MiA8LSBhcy5udW1lcmljKGMoMSwiMiIsMyw0KSkKcHJpbnQobXlBcnJheTIpCmBgYAoKU28sIGlmIHdlIHdhbnQgdG8gb3ZlcnJpZGUgUidzICoqaW1wbGljaXQqKiwgKiphdXRvbWF0aWMqKiB0eXBlIGNvbnZlcnNpb24sIHdoaWNoIGltcGxpZXMgdGhhdCBudW1iZXJzIHdpbGwgYmUgYXV0b21hdGljYWxseSBjb252ZXJ0ZWQgdG8gY2hhcmFjdGVycyBhbmQgbm90IHZpY2UgdmVyc2EsIHdlIG5lZWQgdG8gaW5zdHJ1Y3QgUiB0aGF0IHdlIHdhbnQgYW4gKipleHBsaWNpdCoqIHR5cGUgY29udmVyc2lvbiBieSB1c2luZyB0aGUgYXBwcm9wcmlhdGUgZnVuY3Rpb24sIGBhcy5udW1lcmljKClgIGluIHRoaXMgY2FzZS4KCkZvciBleGFtcGxlOgoKYGBgIHtyIGVjaG8gPSBUfQpsb2dpYyA8LSBjKDAsMSwxLDApCmFzLmxvZ2ljYWwobG9naWMpCmBgYAoKV2hhdCBoYXZlIHdlIGxlYXJuZWQ6IHRoZXJlIGlzIGEgZGF0YSB0eXBlIGNhbGxlZCBgbG9naWNhbGAgaW4gUiAoYWxzbyBrbm93biBhcyAqQm9vbGVhbiopLCBhbmQgaXQgY2FuIHRha2Ugb25seSB0d28gdmFsdWVzOiBgVFJVRWAgYW5kIGBGQUxTRWAgKCoqbm90ZToqKiBhbHdheXMgY2FwaXRhbGl6ZWQgaW4gUikuIFNvLCBgMWAgaXMgYWx3YXlzIGBUUlVFYCwgd2hpbGUgYDBgIGlzIGFsd2F5cyBgRkFMU0VgLCByaWdodD8gTm86CgpgYGAge3IgZWNobyA9IFR9CmxvZ2ljIDwtIGMoMCwzLDEsMCkKYXMubG9naWNhbChsb2dpYykKYGBgCldoZW4gY29udmVydGluZyBudW1iZXJzIHRvIGxvZ2ljYWxzIGluIFIsIGAwYCBpcyBhbHdheXMgYEZBTFNFYC4gVHJ5IGBhcy5sb2dpY2FsKC00KWAsIG9yIGBhcy5sb2dpY2FsKDMuMTQpYCB0byBtYWtlIHN1cmUuCgpOb3csIHdoYXQgaWYgd2Ugd2FudCB0byBtYWludGFpbiBhIGNvbGxlY3Rpb24gb2YgZGF0YSBvZiB2YXJpb3VzIHR5cGVzPyBUaGVuIGxpc3RzIGNvbWUgaW50byBwbGF5OgoKYGBgIHtyIGVjaG8gPSBUfQpteUxpc3QgPC0gbGlzdCgxLCAyLCAiMyIsIDQpCnByaW50KG15TGlzdCkKYGBgCk5vdzoKCi0gdGhlIGxpc3QgaGFzIGZvdXIgZWxlbWVudHM6IGBbWzFdXWAsIGBbWzJdXWAsIGBbWzNdXWAsIGFuZCBgW1s0XV1gOwotIGVhY2ggZWxlbWVudCBjb250YWlucyBhbiBhcnJheSBvZiBsZW5ndGggb25lOiBgMWAsIGAyYCwgYCIzImAsIGFuZCBgNGAsIGFuZAotIHRoZWlyIGRhdGEgdHlwZXMgYXJlOgoKYGBgIHtyIGVjaG8gPSBUfQpjbGFzcyhteUxpc3QpCmBgYAoKT2gsIG5vLCBgY2xhc3MoKWAgcmV0dXJuZWQgdGhlIGRhdGEgdHlwZSBvZiB0aGUgd2hvbGUgbGlzdCEgUiBoYXMgZnVuY3Rpb25zIC0gYSB2ZXJ5IGltcG9ydGFudCBhbmQgcG93ZXJmdWwgc2V0IG9mIGZ1bmN0aW9ucyBpbmRlZWQgLSB0byB3b3JrIHdpdGggbGlzdHMuIExvb2s6CgpgYGAge3IgZWNobyA9IFR9CmxhcHBseShteUxpc3QsIGNsYXNzKQpgYGAKCmBsYXBwbHkoKWAgaXMgYW4gUiBmdW5jdGlvbiB0aGF0ICgxKSBmb3IgYSBnaXZlbiBmdW5jdGlvbiBhcyBpdHMgc2Vjb25kIGFyZ3VtZW50IChgY2xhc3MoKWAgaW4gdGhpcyBleGFtcGxlKSAoMikgcmV0dXJucyBhIHJlc3VsdCBvZiB0aGF0IGZ1bmN0aW9uJ3MgY2FsbCwgdGFraW5nIGVhY2ggZWxlbWVudCBvZiBpdHMgZmlyc3QgYXJndW1lbnQgLSB3aGljaCBpcyBhIGxpc3QsIGBteUxpc3RgIGluIG91ciBleGFtcGxlIC0gYXMgdGhlIHJlc3BlY3RpdmUgZnVuY3Rpb24ncyBpbnB1dCwgYW5kICgzKSByZXR1cm5zIGEgcmVzdWx0IGFzIGEgbGlzdCBvZiB0aGUgbGVuZ3RoIGVxdWFsIHRvIHRoZSBsZW5ndGggb2YgdGhlIGlucHV0IGxpc3QuIAoKTGV0J3MgZXhwZXJpbWVudCB0byB1bmRlcnN0YW5kIGBsYXBwbHkoKWAsIGEgZnVuY3Rpb24gb2YgZXh0cmVtZSBpbXBvcnRhbmNlIGluIFIgcHJvZ3JhbW1pbmcsIGJldHRlcjoKCmBgYCB7ciBlY2hvID0gVH0KbXlMaXN0IDwtIGxpc3QoMSwgMiwgIjMiLCA0KQpsYXBwbHkobXlMaXN0LCBhcy5udW1lcmljKQpgYGAKClNvLCBgbGFwcGx5KClgIGNhbGxlZCBgYXMubnVtZXJpYygpYCBvbiBlYWNoIGVsZW1lbnQgaW4gYG15TGlzdGAgYW5kIHJldHVybmVkIGEgY29udmVydGVkIHZhbHVlLiBBZ2FpbjoKCmBgYCB7ciBlY2hvID0gVH0KbXlMaXN0IDwtIGxpc3QoMSwgMiwgIjMiLCA0KQpsYXBwbHkobXlMaXN0LCBhcy5jaGFyYWN0ZXIpCmBgYAoKQW5kIGFnYWluIGZvciBgYXMubG9naWNhbGA6CgpgYGAge3IgZWNobyA9IFR9Cm15TGlzdCA8LSBsaXN0KDEsIDIsICIzIiwgNCkKbGFwcGx5KG15TGlzdCwgYXMubG9naWNhbCkKYGBgCgpXYWl0LCB3aGF0IGlzIHRoZSBgTkFgIHZhbHVlIGluIGBbWzNdXWA/IFRoZSB0aGlyZCBlbGVtZW50IG9mIHRoZSBgbXlMaXN0YCBsaXN0IGlzIGEgY2hhcmFjdGVyLCBgIjMiYC4gV2UgYXNrZWQgYGxhcHBseSgpYCB0byBhcHBseSBgYXMubG9naWNhbCgpYCB0byBhbGwgbWVtYmVycyBvZiBgbXlMaXN0YC4gSXQgZGlkIHdlbGwgZm9yIHRoZSBudW1iZXJzIChhbGwgcG9zaXRpdmUsIHNvIGFsbCBgVFJVRWApLCBidXQgaXQgZmFpbGVkIGZvciBgIjMiYCB3aGljaCBpcyBhIGNoYXJhY3RlciAtIGFuZCBSIGRvZXMgbm90IGtub3cgaG93IHRvIHRyZWF0IGEgY2hhcmFjdGVyIHZhbHVlIHVuZGVyIGBhcy5sb2dpY2FsYCAtIHNob3VsZCBpdCBiZSBgVFJVRWAgb3IgYEZBTFNFYC4gU2luY2UgYSBjb252ZXJzaW9uIG9mIHRoYXQga2luZCBpcyBub3QgZGVmaW5lZCBpbiBSLCBpdCByZXR1cm5lZCBgTkFgLCBzaG9ydCBmb3IgYE5vdCBBdmFpbGFibGVgLiBZb3Ugd2lsbCBzZWUgYE5BYCB1c2VkIGEgbG90IGluIERhdGEgU2NpZW5jZSBpbiBSLCBhbnl0aW1lIHRoZSB2YWx1ZSBpcywgZm9yIHNvbWUgcmVhc29uLCAqbWlzc2luZyouCgojIyMgNC4gT3VyIG93biBSIGZ1bmN0aW9ucyAKClJlbWVtYmVyIGhvdyBpdCBzdGFydGVkOiBJJ3ZlIHRvbGQgeW91IHRoYXQgeW91IHNob3VsZCB0aGluayBvZiBSIGZ1bmN0aW9ucyBzaW1wbHkgYXMgY29sbGVjdGlvbnMgb2YgUiBjb2RlIHRoYXQgZG9lcyBzb21ldGhpbmcgZm9yIHVzLiBGb3IgZXhhbXBsZSwgcmVwZXRpdGl2ZSB0aGluZ3M6IHRoZXJlIGlzIHNvbWV0aGluZyB0aGF0IGNhbiBiZSBkb25lIHdpdGggZGF0YSwgYnV0IHRoZSBkYXRhIGNhbiB2YXJ5IHdoaWxlIHdoYXQgY2FuIGJlIGRvbmUgdG8gaXQgcmVtYWlucyB0aGUgc2FtZSAtIGFuZCB0aGVuIHdlIG5lZWQgYSBmdW5jdGlvbi4gSXQgaXMgYSBwaWVjZSBvZiBjb2RlIHRoYXQgYWx3YXlzIGRvZXMgb25lIGFuZCB0aGUgc2FtZSB0aGluZyBvdmVyIGl0ICppbnB1dHMqOiB0aGUgdGhpbmdzIHRoYXQgd2UgcGFzcyB0byBpdC4gU2F5LCB3ZSBwYXNzIGAzYCBhbmQgYDRgIHRvIHNvbWUgUiBmdW5jdGlvbiBjYWxsZWQgYG11RnVuKClgIGFuZCB3ZSB3YW50IHRvIGl0IHRvIHJldHVybiB0aGUgcmVzdWx0IG9mIGAzKzRgLCBidXQgd2UgZG8gbm90IHdhbnQgdG8gY2hhbmdlIGl0IGp1c3QgdG8gYmUgYWJsZSB0byBkbyB0aGUgc2FtZSBmb3IgYDFgIGFuZCBgMmAsIG9yIGAxMDAwYCBhbmQgYDMuMTRgLgoKV2UgaGF2ZSBzZWVuIHRoYXQgUiBoYXMgbWFueSBpbi1idWlsdCBmdW5jdGlvbnMuIFdoZW4gd2Ugc3RhcnQgYWRkaW5nIFIgcGFja2FnZXMgdG8gb3VyIHdvcmsgaW4gdGhlIG5leHQgc2Vzc2lvbiB5b3Ugd2lsbCBzZWUgdGhhdCBpdCBhY3R1YWxseSBoYXMgYW4gdW5rbm93biwgdmVyeSwgdmVyeSBsYXJnZSBudW1iZXIgb2YgdGhlbS4gQnV0IGNhbiB3ZSBkZWZpbmUgb3VyIG93biBmdW5jdGlvbnMgaW4gUj8gT2YgY291cnNlLiBJdCBpcyBzaW1wbGU6CgpgYGAge3IgZWNobyA9IFR9Cm15RnVuIDwtIGZ1bmN0aW9uKGEsIGIpIHsKICBzdW1tYSA8LSBhICsgYgogIHJldHVybihzdW1tYSkKfQpgYGAKCk5vdGhpbmcgaGFwcGVucz8gV2UgbmVlZCB0byAqKmNhbGwqKiBhIGZ1bmN0aW9uIGZyb20gUiBpbiBvcmRlciB0byBtYWtlIGl0IHdvcmsuIExvb2s6CgpgYGAge3IgZWNobyA9IFR9Cm15RnVuKDUsIDYpCmBgYAoKTGV0IHVzIGFuYWx5emUgdGhlIGZ1bmN0aW9uIGNvZGUsIGxpbmUgYnkgbGluZToKCi0gYG15RnVuIDwtIGZ1bmN0aW9uKGEsIGIpYCBpcyB0aGUgdmVyeSBmdW5jdGlvbiBkZWZpbml0aW9uOiBpdCB0ZWxscyBSIHRoYXQgd2Ugd2FudCB0byBkZWZpbmUgYSBmdW5jdGlvbiB0aGF0IHRha2VzIHR3byBpbnB1dCBhcmd1bWVudHMsIGBhYCBhbmQgYGJgOwotIGB7YCB0aGF0IGZvbGxvd3MgaXMgaXQgc2lnbmlmaWVzIHRoZSBiZWdnaW5nIG9mIGEgKipjb2RlIGJsb2NrKiogLSBhIGNvbGxlY3Rpb24gb2YgUiBpbnN0cnVjdGlvbnMsIG5ldyBmdW5jdGlvbiBjYWxscywgb3BlcmF0aW9ucywgaXRlcmF0aW9ucywgZXRjLiB0aGF0IHByb2Nlc3MgdGhlIGZ1bmN0aW9uJ3MgYXJndW1lbnRzIGBhYCBhbmQgYGJgIGluIHNvbWUgd2F5OwotIGBzdW1tYSA8LSBhICsgYmA6IHdlIGluc3RhbnRpYXRlIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCBgc3VtbWFgIGluIHRoZSBmdW5jdGlvbiBgbXlGdW5gIGFuZCBhc3NpZ24gaXQgdGhlIHZhbHVlIG9mIHRoZSBleHByZXNzaW9uIGBhICsgYmA7Ci0gYHJldHVybihzdW1tYSlgIHRlbGxzIHRoZSBmdW5jdGlvbiB0byBzdG9wIGl0cyBleGVjdXRpb24gYW5kIHNlbmRzIGJhY2sgdGhlIGFyZ3VtZW50IG9mIHRoZSBmdW5jdGlvbiBgcmV0dXJuKClgIHRvIHRoZSBjYWxsZXIgLSB0aGUgUiBleHByZXNzaW9uIHRoYXQgaGFzIG1hZGUgdGhlIGNhbGwgdG8gYG15RnVuYDsgYW5kIGZpbmFsbHksIAotIGB9YCBjbG9zZXMgdGhlIGNvZGUgYmxvY2sgcHJldmlvdXNseSBvcGVuZWQgd2l0aCBge2AuCgpXaGF0IGhhcHBlbnMgd2hlbiB3ZSBkbzoKCmBgYCB7ciBlY2hvID0gVH0KbXlSZXN1bHQgPC0gbXlGdW4oMi41LCAyLjYpCnByaW50KG15UmVzdWx0KQpgYGAKCmlzIHRoZSBmb2xsb3dpbmcgZmxvdyBvZiBldmVudHM6CgotIHRoZSB2YWx1ZSBvZiBgMi41YCBiaW5kcyB0byB0aGUgZnVuY3Rpb24ncyBgYWAgYXJndW1lbnQ7Ci0gdGhlIHZhbHVlIG9mIGAyLjZgIGJpbmRzIHRvIHRoZSBmdW5jdGlvbidzIGBiYCBhcmd1bWVudDsKLSBgYSArIGJgIGlzIGV4ZWN1dGVkIGluc2lkZSB0aGUgZnVuY3Rpb24gYW5kIGFzc2lnbmVkIHRvIGBzdW1tYWA7Ci0gYHN1bW1hYCBpcyByZXR1cm5lZCB0byB0aGUgY2FsbGVyIGBteVJlc3VsdCA8LSBteUZ1bigyLjUsMi42KWAgd2hlcmUgdGhlIG91dHB1dCBvZiBgbXlGdW5gIC0gYW5kIHRoYXQgaXMgd2hhdCBgcmV0dXJuKHN1bW1hKWAgZGVmaW5lcyAtIGJpbmRzIHRvIG15UmVzdWx0OwotIGBteVJlc3VsdGAgaXMgcHJpbnRlZCBieSBgcHJpbnQoKWAgb3V0c2lkZSB0aGUgZnVuY3Rpb24uCgpSZW1lbWJlciBgbGFwcGx5KClgPyBMZXQncyBwbGF5IGEgYml0OgoKYGBgIHtyIGVjaG8gPSBUfQpteUxpc3QgPC0gbGlzdCgKICAnTmV3IFlvcmssIFVTQScsCiAgJ0JlbGdyYWRlLCBTZXJiaWEnLAogICdNb3Njb3csIFJ1c3NpYScsCiAgJ0JlcmxpbiwgR2VybWFueScsCiAgJ0xvbmRvbiwgVUsnLAogICdQYXJpcywgRnJhbmNlJywKICAnUm9tZSwgSXRhbHknCikKcHJpbnQobXlMaXN0KQpgYGAKCk9rLCB3ZSBoYXZlIGEgbGlzdC4gSSB3YW50IHRvIHdyaXRlIG91dCBhIGZ1bmN0aW9uIHRoYXQgd2lsbCByZXR1cm4gc2VwYXJhdGVseSB0aGUgY2l0eSBhbmQgdGhlIGNvdW50cnkgaW4gd2hpY2ggaXQgcmVzaWRlcyBmcm9tIGBteUxpc3RgLiBIZXJlIGlzIGEgd2F5IHRvIGRvIGl0IHdpdGggb3VyIG1pZ2h0eSBmcmllbmQsIGBsYXBwbHkoKWA6CgpgYGAge3IgZWNobyA9IFR9CmxhcHBseShteUxpc3QsIHN0cnNwbGl0LCBzcGxpdCA9ICIsICIpCmBgYAoKT2gsIG9ubHkgdGhhdCB0aGUgb3V0cHV0IGxvb2tzIGV2ZW4gbW9yZSBjb21wbGljYXRlZCBub3csIHdoYXQgaXMgdGhpczoKCmBgYApbWzddXQpbWzddXVtbMV1dClsxXSAiUm9tZSIgICJJdGFseSIKYGBgCgpJIGtub3cgdGhhdCB0aGlzIG5vdyByZWFsbHkgZmVlbHMgdW5jb21mb3J0YWJsZSwgYnV0IEkgcHJvbWlzZSB5b3UgdGhhdCBpdCBpcyBub3QgdGhhdCBkaWZmaWN1bHQgYXQgYWxsLgoKSXQgcmVhZHMgaW4gUiBpbiB0aGUgZm9sbG93aW5nIHdheTogaXQgaXMgdGhlIHNldmVudGggKGBbWzddXWApIGVsZW1lbnQgb2YgdGhlIG91dHB1dCBmcm9tIGBsYXBwbHlgLCBhbmQgdGhhdCBzZXZlbnRoIGVsZW1lbnQgY29udGFpbnMgb25lIGxpc3QgKGBbWzddXVtbMV1dYCksIGFuZCB0aGFuIHRoYXQgbGlzdCBjb250YWlucyBhbiBhcnJheSB3aXRoIHRoZSBmb2xsb3dpbmcgZWxlbWVudHM6IGAiUm9tZSJgLCBhbmQgIGAiSXRhbHkiLgoKV2h5IHNvIGNvbXBsaWNhdGVkPyBCZWNhdXNlIG9mIHdoYXQgd2UgaGF2ZSBhbHJlYWR5IGxlYXJuZWQ6IGBsYXBwbHkoKWAgcmV0dXJucyBhIGxpc3QsIGJ1dCBgc3Ryc3BsaXQoKWAgKmFsc28qIHJldHVybnMgYSBsaXN0LCBzbyB0aGUgcmVzdWx0IGlzLCBuYXR1cmFsbHksIGEgKmxpc3Qgb2YgbGlzdHMqLiAKCioqTk9URS4qKiBZb3Ugd2lsbCBuZWVkIHRvIG1hbmFnZSBzdWNoIGNvbXBsaWNhdGVkLCBuZXN0ZWQgZGF0YSBzdHJ1Y3R1cmVzIGluIFIgYSBsb3QgaWYgeW91IGFyZSBhYm91dCB0byBlbnRlciBEYXRhIFNjaWVuY2UuIERvbid0IGJlIHNjYXJlZDogaXQgaXMgb25seSBwcmFjdGljZSB0aGF0IHlvdSBhcmUgbWlzc2luZyBhdCB0aGlzIHBvaW50LgoKVGFrZSBhIGNhcmVmdWwgbG9vayB0byBvdXIgY2FsbCB0byBgbGFwcGx5KClgIGFnYWluOgoKYGBgIHtyIGVjaG8gPSBULCBleGVjdXRlID0gRn0KbGFwcGx5KG15TGlzdCwgc3Ryc3BsaXQsIHNwbGl0ID0gIiwgIikKYGBgCgoKV2UgaGF2ZSBhc2tlZCBSIHRvIGBsYXBwbHkoKWAgdGhlIGZ1bmN0aW9uIGBzdHJzcGxpdGAgdG8gYG15TGlzdGAsIGFuZCBpbiB0aGUgZW5kIHBhc3NlZCB0aGUgYXJndW1lbnQgYHNwbGl0YCB3aGljaCBpcyBhY3R1YWxseSBhbiBhcmd1bWVudCBtZWFudCBmb3IgYHN0cnNwbGl0KClgLCBub3QgYGxhcHBseSgpYC4gVGhpcyBpcyB2ZXJ5IGhhbmR5IHdoZW4geW91IG5lZWQgdG8gYGxhcHBseSgpYCBvdGhlciBmdW5jdGlvbnMsIGJ1dCB3aXRoIGEgc3BlY2lmaWMgdmFsdWUgZm9yIHNvbWUgYXJndW1lbnQgdGhhdCB0aGV5IG1pZ2h0IHVzZS4gCgpMZXQncyBub3cgdHJ5IHRvIHNpbXBsaWZ5IHRoZSByZXN1bHQgb2YgdGhpcyBvcGVyYXRpb24gYSBiaXQ6CgpgYGAge3IgZWNobyA9IFR9CnNlcGFyYXRlQ2l0aWVzIDwtIGZ1bmN0aW9uKHN0cmluZykgewogIHJldHVybigKICAgIHN0cnNwbGl0KHN0cmluZywgc3BsaXQgPSAiLCAiKVtbMV1dCiAgKQp9CmxhcHBseShteUxpc3QsIHNlcGFyYXRlQ2l0aWVzKQpgYGAKCk5vdyB0aGF0IGlzIHNpbWlsYXIgdG8gd2hhdCB3ZSBoYXZlIHNlZW4gZnJvbSBgbGFwcGx5KClgIGJlZm9yZS4gSG93IGRpZCB3ZSBhY2hpZXZlIHRoaXM/IExldCcgYW5hbHl6ZSBvdXIgYHNlcGFyYXRlQ2l0aWVzKClgIGZ1bmN0aW9uOgoKYGBgCnNlcGFyYXRlQ2l0aWVzIDwtIGZ1bmN0aW9uKHN0cmluZykgewogIHJldHVybigKICAgIHN0cnNwbGl0KHN0cmluZywgc3BsaXQgPSAiLCAiKVtbMV1dCiAgKQp9CmBgYAoKSW4gdGhpcyBjYXNlLCB0aGVyZSBpcyBubyBzZXBhcmF0ZSAicHJvY2VzcyIgYW5kICJyZXR1cm4gb3V0cHV0IiBzdGVwOiBJIGhhdmUgbmVzdGVkIHRoZSBwcm9jZXNzaW5nIG9mIHRoZSBmdW5jdGlvbidzIGBzdHJpbmdgIGlucHV0IGluIHRoZSBgcmV0dXJuKClgIGNhbGwgYnkgZG9pbmc6IGByZXR1cm4oc3Ryc3BsaXQoc3RyaW5nLCBzcGxpdCA9ICIsICIpW1sxXV0pYC4gSSBzaW1wbHkgZm91bmQgaXQgdG8gYmUgbW9yZSBjb252ZW5pZW50OyBmb3IgYSBmdW5jdGlvbiB3aXRoIGEgbW9yZSBjb21wbGljYXRlZCBjb2RlLCBJIHdvdWxkIHByb2JhYmx5IGF2b2lkIGRvaW5nIHNvbWV0aGluZyBsaWtlcyB0aGlzLiBOb3csIGxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBgc3Ryc3BsaXQoc3RyaW5nLCBzcGxpdCA9ICIsICIpW1sxXV1gIHBhcnQ6IHdoYXQgaXQgZG9lcyBpcyB0aGF0IGl0IGNhbGxzIGBzdHJzcGxpdCgpYCwgcGFzc2luZyBgc3RyaW5nYCBhcyBhbiBpbnB1dCwgYW5kIGRlZmluaW5nIHRoZSB2YWx1ZSBvZiB0aGUgYHNwbGl0YCBhcmd1bWVudCB0byBiZSBgIiwgImAsIGJ1dCBJIGhhdmUgYWxzbyBhZGRlZCBzb21ldGhpbmc6IGBbWzFdXWAgYXQgdGhlIGVuZCBvZiB0aGUgY2FsbCB0byBgc3Ryc3BsaXQoKWAuIFdoeT8gQmVjYXVzZSBJIGtub3cgdGhhdCBgc3Ryc3BsaXQoKWAgcmV0dXJucyBhIGxpc3QgdGhhdCB3aWxsIGVuY29tcGFzcyBhbiBhcnJheSB3aXRoIHRoZSByZXN1bHRzIHRoYXQgSSBleHBlY3QsIHNvIHRoYXQgYnkgc2F5aW5nIHRvIFIgc29tZXRoaW5nIGxpa2UgYHJldHVybihzdHJzcGxpdCh4LCBzcGxpdCA9ICIsICIpW1sxXV0pYCBJIGFtIGFza2luZyBmb3Igd2hhdCBpcyAqKmluc2lkZSoqIHRoZSBsaXN0IHJldHVybmVkIGJ5IGBzdHJzcGxpdGAsIGFuZCBub3QgdGhlIGRpcmVjdCByZXN1bHQgb2YgYHN0cnNwbGl0KHgsIHNwbGl0ID0gIiwgIilgLiBBbmQgd2hhdCBpcyBpbnNpZGUgdGhhdCBsaXN0IGlzIGFuIGFycmF5LiBJZiB5b3Ugc3RpbGwgYXNrIHlvdXJzZWxmIHdoZXJlIGRpZCB0aGUgb3V0cHV0IGxpc3QgY2FtZSBmcm9tIGluIG91ciBgbGFwcGx5KClgIGNhbGw6IGRvbid0IGZvcmdldCB0aGF0IGBsYXBwbHkoKWAgcmV0dXJucyBhIGxpc3QgYnkgaXRzZWxmLiAKClRocmVlIHRoaW5ncyBhbmQgdHJpY2tzIHRvIHJlbWVtYmVyOgoKLSAxLiBgbGFwcGx5KClgIHJldHVybnMgYSBsaXN0OyBgc3Ryc3BsaXQoKWAgcmV0dXJucyBhIGxpc3Q7IGFuZCBtYW55IG90aGVyIFIgZnVuY3Rpb25zIGFzIHdlbGw7IGlmIHlvdSBjb21wb3NlIGEgY2FsbCB0byBvbmUgd2l0aCBhIGNhbGwgdG8gYW5vdGhlciwgZG9uJ3QgZXhwZWN0IHRvIGdldCBhbnl0aGluZyBlbHNlIGJhY2sgYnV0IGEgbGlzdCBvZiBsaXN0czsKCi0gMi4gdGhlIG91dHB1dCBvZiBgbGFwcGx5KClgIGNhbiBiZSBzaW1wbGlmaWVkIGJ5IGEgY2FsbCB0byBpdHMgc2lzdGVyIGZ1bmN0aW9uIGNhbGxlZCBgc2FwcGx5KClgLCBsaWtlIHRoaXM6CgpgYGAge3IgZWNobyA9IFR9CnNlcGFyYXRlQ2l0aWVzIDwtIGZ1bmN0aW9uKHN0cmluZykgewogIHJldHVybigKICAgIHN0cnNwbGl0KHN0cmluZywgc3BsaXQgPSAiLCAiKVtbMV1dCiAgKQp9CnNhcHBseShteUxpc3QsIHNlcGFyYXRlQ2l0aWVzKQpgYGAKCk5vdywgdGhpcyBsb29rcyBiZXR0ZXIgKnNvbWVob3cqIChpdCdzIGEgc29ydCBvZiBhIHRhYnVsYXIgc3RydWN0dXJlLCB0aGUgY2l0aWVzIGFyZSBpbiB0aGUgZmlyc3Qgcm93LCB0aGUgY291bnRyaWVzIGluIHRoZSBzZWNvbmQgcm93KSBidXQgd2Ugc3RpbGwgZG8gbm90IGdldCB0byB1bmRlcnN0YW5kIGV4YWN0bHkgaG93IGBzYXBwbHkoKWAgYWNoaWV2ZWQgdGhpcy4gTW9yZSBvbiBkYXRhIHN0cnVjdHVyZXMgbGlrZSB0aGlzIG9uZSB3aWxsIGJlIHByZXNlbnRlZCBhbmQgZGlzY3Vzc2VkIGluIG91ciBuZXh0IHNlc3Npb25zLCBlc3BlY2lhbGx5IHdoZW4gd2Ugc3RhcnQgdGFsa2luZyBhYm91dCB2ZWN0b3JzLCBtYXRyaWNlcywgYW5kIGNvZGUgdmVjdG9yaXphdGlvbiBpbiBSLgoKLSAzLiBGaW5hbGx5LCBpZiB5b3Ugd2FudCB0byBzdHJpcCB0aGUgbGlzdCBvZiB0aGUgcmVzdWx0IG9mIGEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgbGlzdCBieSBpdHMgZGVmaW5pdGlvbiwgc2ltcGx5IHBpY2sgaXQgdXAgYnkgYFtbYCBhbmQgYF1dYCwgYXMgd2UgZGlkIGluIG91ciBgc3Ryc3BsaXQoKWAgY2FsbDogYHN0cnNwbGl0KHN0cmluZywgc3BsaXQgPSAiLCAiKVtbMV1dYC4KClRvIGVuZCB0aGlzIHNlc3Npb24gd2l0aCBhbiB1bmRlcnN0YW5kaW5nIG9mIG5lc3RlZCBsaXN0cyBpbiBSLCBiZWNhdXNlIEkgY2FuIGltYWdpbmUgbWFueSB3aWxsIGJlIGNvbmZ1c2VkIGJ5IHRoYXQ6CgpgYGAge3IgZWNobyA9IFR9CmEgPC0gbGlzdCgpCmFbWzFdXSA8LSA1CmFbWzJdXSA8LSA3CnByaW50KGEpCmBgYApPaywgbGV0JyBzYXkgdGhpcyBpcyBhICpzaW1wbGUgbGlzdCo6IGl0IGhhcyB0d28gZWxlbWVudHMsIGVhY2ggZWxlbWVudCBoYXMgb25lIG51bWVyaWMgaW4gaXQuIE5vdzoKCmBgYCB7ciBlY2hvID0gVH0KYSA8LSBsaXN0KCkKYVtbMV1dIDwtIGxpc3QoNSwgNykKYVtbMl1dIDwtIGxpc3QoMSwgMykKcHJpbnQoYSkKYGBgCgpMZXQncyByZWFkIGl0IG91dDoKCi0gYGFgIGhhcyB0d28gZWxlbWVudHMsIGluZGV4ZWQgYXMgYFtbMV1dYCAodGhlIGZpcnN0IG9uZSkgYW5kIGBbWzJdXWAgKHRoZSBzZWNvbmQgb25lKTsKLSB0aGUgZmlyc3QgZWxlbWVudCBvZiBgYWAsIHdoaWNoIHdlIHJlZmVyIHRvIGluIFIgYXMgYGFbWzFdXWAsIGlzIGEgbGlzdCBpdHNlbGYsIGFuZCBoYXMgdHdvIGVsZW1lbnRzLCBhZ2FpbnN0IHJlZmVyZW5jZWQgYXMgYFtbMV1dYCBhbmQgYFtbMl1dYCwgdGhlIGZpcnN0IGNvbnRhaW5pbmcgYSBudW1lcmljIGA1YCBhbmQgdGhlIHNlY29uZCBjb250YWluaW5nIGEgbnVtZXJpYyBgN2A7Ci0gdGhlIHR3byBsaXN0cyBuZXN0ZWQgaW4gYGFbWzFdXWAgY2FuIGJlIGFjZXNzZWQgYnkgYGFbWzFdXVtbMV1dYCBhbmQgYGFbWzFdXVtbMl1dYCByZXNwZWN0aXZlbHk6CgpgYGAge3IgZWNobyA9IFR9CmFbWzFdXQpgYGAKCmBgYCB7ciBlY2hvID0gVH0KYVtbMV1dW1sxXV0KYGBgCmBgYCB7ciBlY2hvID0gVH0KYVtbMV1dW1syXV0KYGBgCgphbmQgdGhlIHNhbWUgY2FuIGJlIGRvbmUgZm9yIGBhW1syXV1gOgoKYGBgIHtyIGVjaG8gPSBUfQphW1syXV1bWzFdXQpgYGAKCmBgYCB7ciBlY2hvID0gVH0KYVtbMl1dW1syXV0KYGBgCgpOZXN0ZWQgbGlzdHMgYXJlIG5vdCBjb21wbGljYXRlZCBhdCBhbGwsIGl0IGlzIGp1c3QgYSBtYXR0ZXIgb2YgZ2V0dGluZyB1c2VkIHRvIHRoZSB3YXkgdGhlIFIgc3ludGF4IGlzIHVzZWQgdG8gYWNjZXNzIHRoZWlyIGVsZW1lbnRzLiAKCkZvciBhcnJheXMsIHdlIG9ubHkgdXNlIGBbYCBhbmQgYF1gIHRvIGFjY2VzcyB0aGVpciBlbGVtZW50czoKCmBgYCB7ciBlY2hvID0gVH0KYSA8LSAxOjEwMAphWzUwXQpgYGAKCkFuZCwgKip2ZXJ5IGltcG9ydGFudCoqLCB3ZSBjYW4gc2xpY2Ugb3V0IGVsZW1lbnRzIG9mIGFycmF5czoKCmBgYCB7ciBlY2hvID0gVH0KYVsyNTo1MF0KYGBgCk9uY2UgYWdhaW4sIHRvIGFjY2VzcyBhbiBlbGVtZW50IG9mIGEgbGlzdCwgdXNlIGBbW2AgYW5kIGBdXWA6CgpgYGAge3IgZWNobyA9IFR9CmEgPC0gbGlzdCgxLDIsMyw0LDUpCmFbWzRdXQpgYGAKCkJ1dCB0byBzbGljZSBvdXQgYSBwYXJ0IG9mIHRoZSBsaXN0IC0gYSByYW5nZSBvZiBpdHMgZWxlbWVudHMsICoqZG8gbm90IHVzZSoqIGBbW2AgYW5kIGBdXWAsIGJ1dCBvbmx5IGBbYCBhbmQgYF1gOgoKYGBgIHtyIGVjaG8gPSBUfQphIDwtIGxpc3QoMSwyLDMsNCw1KQphWzM6NF0KYGBgCgpGaW5hbGx5LCB5b3Ugd2lsbCBsb3ZlIHRoZSBmYWN0IHRoYXQgdGhpbmdzIGxpa2UgYGxhcHBseSgpYCBjYW4gYmUgdXNlZCBvbiB2ZWN0b3JzIHRvbzoKCmBgYCB7ciBlY2hvID0gVH0KYSA8LSBjKDEsMiwzLDQpCmxhcHBseShhLCAiXiIsIDIpCmBgYAoKRGlkIHlvdSBsaWtlIGl0PyBIZXJlIGlzIHdoYXQgSSBkaWQ6CgotIHRoZXJlIGlzIGFuIGFycmF5IGBhYCwKLSB0aGVuIGEgYGxhcHBseSgpYCBjYWxsIHRvIGNhbGwgdGhlIGZ1bmN0aW9uICJeIiAob3RoZXIgbGFuZ3VhZ2VzIHdvdWxkIGNhbGwgIl4iIGFuIG9wZXJhdG9yIC0gc29tZSByZWFzb25zIHdoeSBJIHNpbXBseSBhZG9yZSBSIGFuZCBpdHMgZnVuY3Rpb25hbCBuYXR1cmUpIGFuZCBwYXNzIGl0IGEgc2Vjb25kIHBhcmFtZXRlciBgMmAuIAoKVGhlIHJlc3VsdHMgaXMgYSBsaXN0IG9mIGVsZW1lbnRzIGluIGBhYCBhbGwgcmFpc2VkIG9uIHBvd2VyIG9mIGAyYC4gCgpgYGAge3IgZWNobyA9IFR9CmFbWzNdXQphW1szXV1eMgpgYGAKCkVuam95IHRoaXM6CgpgYGAge3IgZWNobyA9IFR9CjVeMiAKYGBgCmBgYCB7ciBlY2hvID0gVH0KIl4iKDUsIDIpIApgYGAKCiMjIyA1LiBgZGF0YS5mcmFtZWAKCkxpc3RzIGFyZSBub3QgdGhlIG9ubHkgYmVpbmdzIGluIFIgdGhhdCBjYW4gaG9sZCB0b2dldGhlciB0aGluZ3Mgb2YgZGlmZmVyZW50IHR5cGVzLgoKSW50cm9kdWNpbmcgYGRhdGEuZnJhbWVgLCB0aGUgZGF0YSB0eXBlIHRoYXQgbWFrZXMgRGF0YSBTY2llbmNlIHBvc3NpYmxlOgoKYGBgIHtyIGVjaG8gPSBUfQojIC0gQ29tbWVudDogdGhlIHBvcHVsYXRpb24gZGF0YSByZXByZXNlbnQgdGhlIG1vc3QgcmVjZW50bHkgdXBkYXRlZCBmaWd1cmVzCiMgLSBmcm9tIFdpa2lkYXRhOiBodHRwczovL3d3dy53aWtpZGF0YS5vcmcvCgojIC0gbGV0J3MgcHJvZHVjZSBzZXZlcmFsIHZlY3RvcnM6CmNpdGllcyA8LSBjKCdOZXcgWW9yaycsICdCZWxncmFkZScsICdNb3Njb3cnLCAnUGFyaXMnLCAnUm9tZScsICdCZXJsaW4nKQpjb3VudHJpZXMgPC0gYygnVVNBJywgJ1NlcmJpYScsICdSdXNzaWEnLCAnRnJhbmNlJywgJ0l0YWx5JywgJ0dlcm1hbnknKQpwb3B1bGF0aW9ucyA8LSBjKDg0MDU4MzcsICAxMzc4NjgyLCAxMjY5MjQ2NiwgMjE4NzUyNiwgMjg3MjgwMCwgMzY0NDgyNikKbGF0dGl0dWRlcyA8LSBjKDQwLjczMDYxMCwgIDQ0Ljc4NzE5NywgNTUuNzUxMjQ0LCA0OC44NjQ3MTYsICA0MS45MDI3ODIsIDUyLjUyMDAwOCkKbG9uZ2l0dWRlcyA8LSBjKC03My45MzUyNDIsIDIwLjQ1NzI3MywgMzcuNjE4NDIzLCAyLjM0OTAxNCwgMTIuNDk2MzY2LCAxMy40MDQ5NTQpCgojIC0gbm93IGxldCdzIGhhdmUgYSBkYXRhLmZyYW1lOgpteUNpdGllcyA8LSBkYXRhLmZyYW1lKGNpdHkgPSBjaXRpZXMsCiAgICAgICAgICAgICAgICAgICAgICAgY291bnRyeSA9IGNvdW50cmllcywgCiAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiA9IHBvcHVsYXRpb25zLCAKICAgICAgICAgICAgICAgICAgICAgICBsYXQgPSBsYXR0aXR1ZGVzLCAKICAgICAgICAgICAgICAgICAgICAgICBsb24gPSBsb25naXR1ZGVzLCAKICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIC0gZGlzcGxheSBteUNpdGllczoKcHJpbnQobXlDaXRpZXMpCmBgYAoKSG93IGRvIHdlIGFjY2VzcyBkaWZmZXJlbnQgY29sdW1ucyBpbiBhIGBkYXRhLmZyYW1lYD8gRWFzeToKCmBgYCB7ciBlY2hvID0gVH0KbXlDaXRpZXMkY2l0eQpgYGAKCmBgYCB7ciBlY2hvID0gVH0KbXlDaXRpZXMkcG9wdWxhdGlvbgpgYGAKCmBgYCB7ciBlY2hvID0gVH0KcHJpbnQobXlDaXRpZXNbICwgYygnbGF0JywgJ2xvbicpXSkKYGBgCgoqKk5PVEUuKiogTWluZCB0aGUgdXNhZ2Ugb2YgYFsgLGAgaW4gdGhlIHByZXZpb3VzICoqc3Vic2V0dGluZyoqIG9mIHRoZSBgbXlDaXRpZXNgIGBkYXRhLmZyYW1lYDogd2hhdCBpZiBJIGFza2VkIGZvciBgbXlDaXRpZXNbMSwgYygnbGF0JywgJ2xvbicpXWAgb25seToKCmBgYCB7ciBlY2hvID0gVH0KbXlDaXRpZXNbMSwgYygnbGF0JywgJ2xvbicpXQpgYGAKCkFuZCB3aGF0IGlmIGFza2VkIGZvcjoKCmBgYCB7ciBlY2hvID0gVH0KbXlDaXRpZXNbMToyLCBjKCdsYXQnLCAnbG9uJyldCmBgYAoKQnV0IHdoZW4gSSBzYXkgYFsgLCBjKCdsYXQnLCAnbG9uJyldYCwgdGhhdCBtZWFucyB0aGF0IEkgd2FudCAqKmFsbCoqIHRoZSByb3dzLCBhbmQgb25seSB0aGUgYGMoJ2xhdCcsICdsb24nKWAgY29sdW1uczoKCmBgYCB7ciBlY2hvID0gVH0KbXlDaXRpZXNbICwgYygnbGF0JywgJ2xvbicpXQpgYGAKCldlIHdpbGwgYmUgbGVhcm5pbmcgKiphIGxvdCoqIGFib3V0IGBkYXRhLmZyYW1lc2AgaW4gb3VyIGZ1dHVyZSBzZXNzaW9ucy4gCgojIyMgNi4gVGhlIHNoYXBlIG9mIHRoaW5ncyB0byBjb21lOiBiZWF1dHkgYW5kIHNpbXBsaWNpdHkKCiMjIyMgNi5BIEV4YW1wbGU6IEludGVyYWN0aXZlIGdlby12aXN1YWxpemF0aW9uIGluIDkgbGluZXMgb2YgY29kZQoKTm93LCBhbGxvdyBtZSB0byBzaG93IHlvdSBob3cgcG93ZXJmdWwgUiByZWFsbHkgaXMuIEhvdyBtYW55IGxpbmVzIG9mIFIgY29kZSBkbyBJIG5lZWQgdG8gY3JlYXRlIGFuIGludGVyYWN0aXZlIG1hcCB0aGF0IHNpbmdsZXMgb3V0IGNpdGllcyBmb3VuZCBpbiB0aGUgYG15Q2l0aWVzYCBgZGF0YS5mcmFtZWAgYnkgbWFya2VycyB0aGF0IGRpc3BsYXkgaW5mb3JtYXRpb24gb24gdGhlbSB3aGVuIGNsaWNrZWQgdXBvbj8gTGV0J3Mgc2VlOgoKYGBgIHtyIGVjaG8gPSBULCAgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGLCBmaWcud2lkdGggPSA5fQojIC0gU2V0dXAKIyAtIEluc3RhbGwge2xlYWZsZXR9IGlmIG5vdCBpbnN0YWxsZWQ7CiMgLSBpbnN0YWxsLnBhY2thZ2VzKCdsZWFmbGV0JykKIyAtIHtkcGx5cn0gc2hvdWxkIGJlIHByZXNlbnQgYWxyZWFkeQoKIyAtIGxvYWQgUiBwYWNrYWdlcwpsaWJyYXJ5KGxlYWZsZXQpCmxpYnJhcnkoZHBseXIpCiMgYSBzaW5nbGUgaWNvbiBpcyBkZWNsYXJlZAphd2Vzb21lIDwtIG1ha2VBd2Vzb21lSWNvbigKICBpY29uID0gImluZm8iLCBpY29uQ29sb3IgPSAiYmxhY2siLCBtYXJrZXJDb2xvciA9ICJibHVlIiwgbGlicmFyeSA9ICJmYSIKKQpsZWFmbGV0KGRhdGEgPSBteUNpdGllcykgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRBd2Vzb21lTWFya2Vycyh+bG9uLCB+bGF0LCBpY29uID0gYXdlc29tZSwgCiAgICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+YXMuY2hhcmFjdGVyKHBvcHVsYXRpb24pLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSB+YXMuY2hhcmFjdGVyKGNpdHkpKQpgYGAKClRoYXQgd291bGQgYmUgdGVuICgxMCkgbGluZXMgb2YgY29kZSwgbm90IGNvdW50aW5nIHRoZSBjb21tZW50cyBvZiBjb3Vyc2UuIAoKIyMjIyA2LkIgRXhhbXBsZTogUmFwaWQgdmlzdWFsaXphdGlvbnMgdy4ge2dncGxvdDJ9CgpBIGZhbW91cyBkYXRhc2V0IHVzZWQgaW4gRGF0YSBTY2llbmNlIHRyYWluaW5ncywgYG10Y2Fyc2AsIGVudGVycywgdG9nZXRoZXIgd2l0aCB0aGUgaW5kdXN0cmlhbCBzdGFuZGFyZCB2aXN1YWxpemF0aW9uIGxpYnJhcnkgYHtnZ3Bsb3QyfWA6CgpgYGAge3IgZWNobyA9IFQsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0KIyAtIGluc3RhbGwge2dncGxvdDJ9IGlmIG5vdCBpbnN0YWxsZWQKIyAtIGluc3RhbGwucGFja2FnZXMoJ2dncGxvdDInKQojIC0gaW5zdGFsbCB7Z2dyZXBlbH0gaWYgbm90IGluc3RhbGxlZAojIC0gaW5zdGFsbC5wYWNrYWdlcygnZ2dyZXBlbCcpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpkYXRhKG10Y2FycykKbXRjYXJzJG1vZGVsIDwtIHJvdy5uYW1lcyhtdGNhcnMpCm10Y2FycyRnZWFyIDwtIGFzLmZhY3RvcihtdGNhcnMkZ2VhcikKcHJpbnQobXRjYXJzKQpgYGAKCmBgYCB7ciBlY2hvID0gVH0KZ2dwbG90KGRhdGEgPSBtdGNhcnMsIAogICAgICAgYWVzKHggPSBtcGcsIHkgPSBxc2VjLAogICAgICAgICAgIGxhYmVsID0gbW9kZWwsIAogICAgICAgICAgIGdyb3VwID0gZ2VhciwgCiAgICAgICAgICAgY29sb3IgPSBnZWFyKSkgKyAKICBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgCiAgICAgICAgICAgICAgc2l6ZSA9IC4xNSkgKyAKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsgCiAgZ2d0aXRsZSgibXBnIHZzLiBxc2VjIGluIHRoZSBtdGNhcnMgZGF0YXNldCwgcGVyIGdlYXIuIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpCdXQgaWYgeW91IHdhbnQgdG8gYmUgc3VyZSB0aGF0IHlvdSBjYW4gdXNlIHRoaXMgcG93ZXIgaW4gYSByZXNwb25zaWJsZSB3YXksIHlvdSBuZWVkIHRvIGdldCBhbG9uZyB3aXRoIHZlY3RvcnMsIGxpc3RzLCBuZXN0ZWQgbGlzdHMsIGZ1bmN0aW9ucywgY29kZSB2ZWN0b3JpemF0aW9uLCBhbmQgbWFueSBvdGhlciB0aGluZ3MgYXMgd2VsbC4KClN0YXkgdHVuZWQuIFdlIGRpZG4ndCBldmVuIHN0YXJ0IHRoZSBkYW5jZSB5ZXQuCgojIyMgRnVydGhlciBSZWFkaW5ncwoKLSBbQ2hhcHRlciAxIFN0YXJ0aW5nIG91dCBpbiBSIGZyb20gSW50cm9kdWN0aW9uIHRvIFIsIHZlcnNpb25dKGh0dHBzOi8vbW9uYXNoZGF0YWZsdWVuY3kuZ2l0aHViLmlvL3ItaW50cm8tMi9zdGFydGluZy1vdXQtaW4tci5odG1sI3NhdmluZy1jb2RlLWluLWFuLXItc2NyaXB0KQotIFtDaGFwdGVyIERhdGEgVHlwZXMgYW5kIFN0cnVjdHVyZXMgZnJvbSBQcm9ncmFtbWluZyB3aXRoIFJdKGh0dHBzOi8vc3djYXJwZW50cnkuZ2l0aHViLmlvL3Itbm92aWNlLWluZmxhbW1hdGlvbi8xMy1zdXBwLWRhdGEtc3RydWN0dXJlcy8pCi0gW0NoYXB0ZXIgNCBXb3JrZmxvdzogYmFzaWNzIGZyb20gUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3dvcmtmbG93LWJhc2ljcy5odG1sKQoKIyMjIEhpZ2hseSBSZWNvbW1lbmRlZCBUbyBEbwoKLSBSZWFkIFtDaGFwdGVycyAxIHRvIDMgZnJvbSBSIGZvciBEYXRhIFNjaWVuY2UsIEhhZGxleSBXaWNraGFtICYgR2FycmV0dCBHcm9sZW11bmRdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKQotIFJlYWQgW0NoYXB0ZXJzIDEgdG8gNSBmcm9tIE5vcm1hbiBNYXRsb2Zm4oCZcyBUaGUgQXJ0IG9mIFIgUHJvZ3JhbW1pbmddKGh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vc2VhcmNoP2NsaWVudD1maXJlZm94LWItZCZjaGFubmVsPXRyb3cyJnN4c3JmPUFMZUtrMDNUX3FMQ016UklDWVdqNVVIcUZCSG52ZlY2VXclM0ExNjA3MjIwNzk1NTUzJmVpPU96N01YOGFlSVlLNmt3V2M1SmJnQXcmcT1Ob3JtYW4rTWF0bG9mZitUaGUrQXJ0K29mK1IrUHJvZ3JhbW1pbmcrcGRmJm9xPU5vcm1hbitNYXRsb2ZmK1RoZStBcnQrb2YrUitQcm9ncmFtbWluZytwZGYmZ3NfbGNwPUNnWndjM2t0WVdJUUF6SUZDQUFReVFNNkJ3Z2pFTWtERUNjNkFnZ3VVSms5V09kQVlLUkJhQUJ3QUhnQWdBR0hBWWdCOVFPU0FRTXdMalNZQVFDZ0FRR3FBUWRuZDNNdGQybDZ3QUVCJnNjbGllbnQ9cHN5LWFiJnZlZD0wYWhVS0V3aUdxT0NFcExqdEFoVUMzYVFLSFJ5eUJUd1E0ZFVEQ0F3JnVhY3Q9NSkKCiMjIyBSIE1hcmtkb3duCgpbUiBNYXJrZG93bl0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKSBpcyB3aGF0IEkgaGF2ZSB1c2VkIHRvIHByb2R1Y2UgdGhpcyBiZWF1dGlmdWwgTm90ZWJvb2suIFdlIHdpbGwgbGVhcm4gbW9yZSBhYm91dCBpdCBuZWFyIHRoZSBlbmQgb2YgdGhlIGNvdXJzZSwgYnV0IGlmIHlvdSBhbHJlYWR5IGZlZWwgcmVhZHkgdG8gZGl2ZSBkZWVwLCBoZXJlJ3MgYSBib29rOiBbUiBNYXJrZG93bjogVGhlIERlZmluaXRpdmUgR3VpZGUsIFlpaHVpIFhpZSwgSi4gSi4gQWxsYWlyZSwgR2FycmV0dCBHcm9sZW11bmRzLl0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgCgoKCioqKgpHb3JhbiBTLiBNaWxvdmFub3ZpxIcKCkRhdGFLb2xla3RpdiwgMjAyMC8yMQoKY29udGFjdDogZ29yYW4ubWlsb3Zhbm92aWNAZGF0YWtvbGVrdGl2LmNvbQoKIVtdKC4uL19pbWcvREtfTG9nb18xMDAucG5nKQoKKioqCkxpY2Vuc2U6IFtHUEx2M10oaHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzL2dwbC0zLjAudHh0KQpUaGlzIE5vdGVib29rIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3IgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4KVGhpcyBOb3RlYm9vayBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLCBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZiBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCllvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFsb25nIHdpdGggdGhpcyBOb3RlYm9vay4gSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LgoKKioqCgo=