A gentle intro to programming and data analysis using R

This is a browser-friendly version of the R script we used during the tutorial session. You can download the R Markdown file that contains all the code in this document by selecting “download Rmd” in the dropdown at the top right of this page. Or, you can follow along by typing the commands into a .R file from within a GUI like R Studio (the one I’d recommend).

Section 1:

R works with an interpreter, which means that you can run, aka execute, a single command at a time (e.g. from within R Studio). A command is just a line or block of actual code – in other words, a command is just a line or block of a script that doesn’t start with the character #. To run a command is to send it to the interpreter.

Lines that start with # are called comments, and are intended to be interpreted by other humans (or yourself at a later time) – comments are ignored by the interperter if you run the entire script all at once.

When you run a command, the output (if there’s any) will appear in the console, aka command line, which you can see below in RStudio.

Now we’ll start a new section, which is not necessary but is good practice.

Section 1: our first commands, and loading/installing packages

Let’s start with some basic commands.

Here is our first command – execute it with cmd+Enter on Mac, or ctrl+Enter on PC. In the web-version, the output will appear in a separate box just below the command.

3 + 5
[1] 8

The output box line indicates that the first (and only) element "[1]" of the output is 8, so the interpreter returned the number 8 given the command 5+3. That seems basically right. Nice.

We can do all kinds of mathematical calculations, e.g.

3 + ((1 - (5 * 2)) / (3 ^ 2))
[1] 2

In R, +, -, /, *, and ^ are functions (operators, technically) – these functions take two arguments which are numbers, and return a third number – the value of the function applied to the two arguments.

But most functions in R have a different syntax – they take their arguments in a more function-y notation (’member “f(x)” from high school math?). for example sqrt() takes one argument (a number) and returns the square root of that argument:

sqrt(9) 
[1] 3

The functions max() and min() can take any number of arguments, and return the highest/lowest of them:

max(1, 2, 3, 5, 4, 5, 6, 6, -10)
[1] 6
min(1, 2, 3, 5, 4, 5, 6, 6, -10)
[1] -10

You can always pull up the help page for a function by saying ?<function-name>(). you can search help pages with ??<topic>

?max()
??linguistics

One of the best things about R is that anyone can contribute helfpul, New user-defined functions and methods are bundled in packages – collections of functions and other stuff that aren’t part of what automatically comes with R when you install it (aka base R).

You should check out the languageR package, which is a companion to the excellent 2007 textbook by Harald Baayen, Analyzing Linguistic Data: A Practical Introduction to Statistics. (psa: you juuuust might be able to find a bootleg version if you look online)

If you are using a particular package for the first time, you will have to install it, which can be done with install.packages("<package name>") note quotes around the name, which are necessary when installing

# install.packages("languageR")

After a package is installed, you load it with library()

library("languageR") 

You can see your library – a list of your installed packages – by saying library(), without an argument. You can see which packages are currently loaded with search(), again with no argument. We will use some functions from non-base packages in section 6 below

Section 2: variables and assignments

Most of the time we don’t just want to calculate something, but we want to calculate something and store the result for later use. We do this by assigning the result of a command to a variable such as x by using the assignment operator = or <-

x = 5 + 3

It is safer to use <- for assignment than =, but I think = is more intuitive, so we will use it here (see sec 7 for discussion).

Notice in the upper right pane of RStudio, you can now see x is saved. We can look at the value of the variable x by simply sending x to the interpreter

x
[1] 8

We can store the results of basically anything in a variable, and we can (and should) use useful names for variables

biggest
[1] 10

Make sure your variable names don’t contain math operators like - or +, and make sure they don’t start with a number: we can name a var ‘var1’

var1 = 1 + 1

But not 1stVar, bc it starts with a number:

# if we try:    1stVar = 1 + 1
# we will get:  Error: unexpected symbol in "1stVar"

Errors and error messages are annoying but extremely important + useful!

To avoid clutter, we can remove vars from the workspace when we are done working w them

rm(var1)
rm(biggest)
rm(x)

A common pitfall is to confuse = (assignment, argument binding) with == (equals).

##if we try:    5 + 3 = 8
##we will get:  
## Error in 5 + 3 = 8 : target of assignment expands to non-language object
# 1 <- 1

To say what we meant, we would use ==

5 + 3 == 8
[1] TRUE

Section 3: data types and data structures

We’ve done some basic math, which is basically manipulating numbers. But usually we want to do a lot more than that – we want to manipulate character strings (e.g. words) too. This is made possible by the existence of different data types. We can see the type of an object by using

class(x)
[1] "numeric"

let’s give x a new value + see its type (character string)

class(x)
[1] "character"

logical (true/false; binary) is also a basic data type:

class(x)
[1] "logical"

Usually we work with more complicated data structures than strings or numbers.

vec1
[1] "boosh1" "boosh2" "boosh3"
lis1
[[1]]
[1] "boosh1"

[[2]]
[1] "boosh2"

[[3]]
[1] "boosh3"
identical(vec1,lis1)
[1] FALSE

But the most important data structure in R is the data frame

df = data.frame(
  col1=letters[1:6],                      # gives us c("a","b","c","d","e","f")
  col2=1:6,                               # same as c(1,2,3,4,5,6)
  col3=rep(c("group1","group2"), times=3) # repeat "group1","group2" thrice
)

Note that argument places have names, e.g. the function rep() has an argument called times, which indicates the number of times that the first argument to rep() should be repeated. We saturated times with 3 above, yielding a vector of length 6.

A data.frame() is for the most part just a list of vectors v1,v2,…,vn such that length(v1)==length(v2)====length(vn)

is.list(df)
[1] TRUE
is.data.frame(df)
[1] TRUE

But you should think of a dataframe as a spreadsheet that contains data, usually where each row is a sequence of facts about a single thing

df
  col1 col2   col3
1    a    1 group1
2    b    2 group2
3    c    3 group1
4    d    4 group2
5    e    5 group1
6    f    6 group2

Now let’s clean up the workspace before moving on (doing this saves memory + can help keep your agenda clear). Instead of starting a new line, we can indicate the end of a command with ;

rm(df); rm(lis1); rm(vec1); rm(x)

Section 4: Data frames and reading files

Let’s check out the frequency list we used for homework #4. We’ll read it in with read.csv() and store it in a var called words.

words <- read.csv(file="top5k-word-frequency-dot-info.csv",
                  sep=",", header=TRUE)

read.csv() gives us a data frame (henceforth df).

class(words)
[1] "data.frame"

The first thing we should do when reading in a new df is check it out

str(words)
'data.frame':   5000 obs. of  5 variables:
 $ Rank        : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Word        : Factor w/ 4352 levels "a","abandon",..: 3911 340 160 2605 1 1961 3972 1806 3972 2100 ...
 $ PartOfSpeech: Factor w/ 14 levels "a","c","d","e",..: 1 13 2 5 1 5 11 13 5 9 ...
 $ Frequency   : int  22038615 12545825 10741073 10343885 10144200 6996437 6332195 4303955 3856916 3872477 ...
 $ Dispersion  : num  0.98 0.97 0.99 0.97 0.98 0.98 0.98 0.97 0.99 0.96 ...
# get a summary of each col of the df
summary(words) 
      Rank           Word       PartOfSpeech    Frequency       
 Min.   :   1   like   :   5   n      :2542   Min.   :    4875  
 1st Qu.:1251   no     :   5   v      :1001   1st Qu.:    7723  
 Median :2500   light  :   4   j      : 839   Median :   13698  
 Mean   :2500   past   :   4   r      : 340   Mean   :   65959  
 3rd Qu.:3750   top    :   4   i      :  97   3rd Qu.:   31474  
 Max.   :5000   as     :   3   p      :  46   Max.   :22038615  
                (Other):4975   (Other): 135                     
   Dispersion    
 Min.   :0.4700  
 1st Qu.:0.9200  
 Median :0.9400  
 Mean   :0.9324  
 3rd Qu.:0.9500  
 Max.   :0.9900  
                 

we can also look at the first n rows by calling head() and specifying n

head(words, n=5)
  Rank Word PartOfSpeech Frequency Dispersion
1    1  the            a  22038615       0.98
2    2   be            v  12545825       0.97
3    3  and            c  10741073       0.99
4    4   of            i  10343885       0.97
5    5    a            a  10144200       0.98

in RStudio only we can use View() to see the whole thing in a window (can also click on the df in the upper righthand pane)

View(words)

Section 5: Basic operations on data frames

The individual cols of a df are referred to “variables” (confusing, ik!) and they can be accessed by name with the $ operator

wordCol <- words$Word

Just view the first 10 elements of a vector with <vec-name>[1:10]

wordCol[1:10]
 [1] the  be   and  of   a    in   to   have to   it  
4352 Levels: a abandon ability able abortion about above ... zone
rm(wordCol)

We can access a row or range of rows using the [ , ] operator (for rows, before the comma)

words[1:2,]
  Rank Word PartOfSpeech Frequency Dispersion
1    1  the            a  22038615       0.98
2    2   be            v  12545825       0.97

Also works for cols (after the comma)

head(words[,1:2], n=5)
  Rank Word
1    1  the
2    2   be
3    3  and
4    4   of
5    5    a

We can add new cols by combining $ with the assignment operator

words$newCol <- words$Frequency / words$Rank
head(words, n=5)
  Rank Word PartOfSpeech Frequency Dispersion   newCol
1    1  the            a  22038615       0.98 22038615
2    2   be            v  12545825       0.97  6272912
3    3  and            c  10741073       0.99  3580358
4    4   of            i  10343885       0.97  2585971
5    5    a            a  10144200       0.98  2028840

we can remove cols we no longer need or don’t want by assigning them to NULL

words$newCol <- NULL

We can also change the data type of a given col. words$PartOfSpeech is a factor, meaning a discrete category with a specified set of possible values – i.e. levels

class(words$PartOfSpeech)
[1] "factor"
levels(words$PartOfSpeech)
 [1] "a" "c" "d" "e" "i" "j" "m" "n" "p" "r" "t" "u" "v" "x"

We could change p.o.s. to character type with

words$PartOfSpeech <- as.character(words$PartOfSpeech)
class(words$PartOfSpeech)
[1] "character"

But we should change it back because p.o.s. actually is a factor

words$PartOfSpeech <- as.factor(words$PartOfSpeech) 

It is often useful to subset a df if we are interested in only some part of it – usually some subset of the rows. Suppose we only want to look at info about the 100 most frequent words. We could carve out the top 100 by saying any of the following:

frequentWords <- subset(words, Rank %in% 1:100)
frequentWords <- subset(words, Rank < 101)
frequentWords <- subset(words, Rank <= 100)

There are several functions that can be used for subsetting we can also use a more primitive (but safer + ultimately better) way:

frequentWords <- words[words$Rank %in% 1:100, ]
frequentWords <- words[words$Rank < 101, ]
frequentWords <- words[words$Rank <= 100, ]
rm(frequentWords)

You can see that these methods yield the same result:

identical(subset(words, Rank < 101), words[words$Rank < 101, ])
[1] TRUE

Now suppose we only want to look at nouns. then we could define nouns as

nouns <- subset(words, PartOfSpeech=="n")

Here is a tricky fact: when you subset a df, you retain all original factor levels, even if they aren’t instantiated in the subset

levels(nouns$PartOfSpeech)
 [1] "a" "c" "d" "e" "i" "j" "m" "n" "p" "r" "t" "u" "v" "x"
levels(nouns$Word)[1:10]
 [1] "a"        "abandon"  "ability"  "able"     "abortion" "about"   
 [7] "above"    "abroad"   "absence"  "absolute"

We usually want to eliminate factor levels that do not occur in the subset:

nouns <- droplevels(nouns)

You can also wrap subset() with droplevels() in the subsetting call itself:

nouns <- droplevels(subset(words, PartOfSpeech=="n"))

Section 6: summary statistics and visualizing data

We can get the average of a numeric vector (incl. a df column) using mean()

mean(words$Rank)
[1] 2500.5

Sometimes df’s will have missing values, indicated with NA if we try to take the mean of a vector containing NA, we get NA as a result, even if 99% of the vector has legitimate numbers in it.

mean(c(1,2,3,NA))
[1] NA

So it is often safest to set the na.rm argument to TRUE, as in:

mean(c(1,2,3,NA), na.rm=TRUE)
[1] 2

We can also see more detailed info of a col with summary()

summary(words$Frequency)
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
    4875     7723    13700    65960    31470 22040000 

We can look at a table of a factor col or see a summary

table(words$PartOfSpeech)

   a    c    d    e    i    j    m    n    p    r    t    u    v    x 
  11   38   34    1   97  839   35 2542   46  340    1   13 1001    2 

We can look at the distribution of a numeric var with a histogram:

We can make a scatterplot of two numeric variables.

Waow looks like a strong relationship…Actually, we are looking at a visual example of Zipf’s Law!!!

Zipf’s Law states that word-frequency is inversely related to frequency rank – we can see that the linear correlation between frequency and rank is very strong; if we apply a log transformation, the relationship is about as perfect as you will ever see in an empirical domain.

cor(nounsFreq$Rank, nounsFreq$Frequency)
[1] -0.7756254
cor(log(nounsFreq$Rank), log(nounsFreq$Frequency))
[1] -0.9973444

End tangent into Zipf’s law.

We can also plot subsets of a numeric variable for each level of a factor

This plot is very informative but not very intuitive to read. What about something easier to understand, like a bar graph? Specifically, what would be nice is if for each part of speech, we had a bar whose height is equal to the average rank of words with that part of speech, within our dataset.

To make the visualization more meaningful, let’s cut the df so that it contains only: nouns, verbs, adjectives, prepositions, articles, and pronouns.

nvap <- subset(words, PartOfSpeech %in% c("n","v","j","i","a","p"))

There are a number of ways to make the plot we are aiming for. We will do it in a way that will illustrate how to execute another important and common task in R: aggregation. We can use the function aggregate() to calculate the mean rank for each subset of the data defined by a level of nvap$PartOfSpeech (there are other ways to do this as well). The result of this aggregate() call will be a df containing the group means:

meanRanks
  PartOfSpeech       Rank
1            a   58.54545
2            i 1581.38144
3            j 2720.49464
4            n 2613.92132
5            p  947.93478
6            v 2435.96404

Now that we have our means, we can make a plot of them. If you ever get more into R, you will want to use the amazing visualization library ggplot2:: (for “grammar of graphics”)

And here’s the plot (I won’t explain ggplot2 syntax here, it is a bit less intuitive than the base graphics capabilities, but well-worth learning).

Interesting…What does the plot tell us about frequency and parts of speech?

# sec 6 cleanup
rm(nvap); rm(meanRanks); rm(nounsFreq); rm(nouns)

Section 7: stuff to know about R syntax

Using = for var assignment is actually not a good practice (for reasons you don’t need to understand at this point – ask me if you’re interested)

We should be using <-, which is unfortunate bc it less obvious + intuitive. The reason is that, while = and <- have some common uses, both have uses such that, if we use the other one instead, things will break. Ask me more if you are interested.

x <- 5 + 3

However, you should still use = when you are specifying an argument in a function call, e.g. in the following say n=10, not n<-10

head(words, n=3)
  Rank Word PartOfSpeech Frequency Dispersion
1    1  the            a  22038615       0.98
2    2   be            v  12545825       0.97
3    3  and            c  10741073       0.99

Generally speaking, spaces and brackets are optional in R, but make code more readable

5+3/2^2-4 == 5 + (3 / 2^2) - 4
[1] TRUE
identical(x<-5+3, x <- 5 + 3)
[1] TRUE
identical(max(1, 2, 3), max(1,2,3))
[1] TRUE

But be careful – the assignment operator <- is a unit + can’t be split up! The first line will be interpreted as the (false) statement that the value of the variable ‘x’ is less than the sum of -5 an 3 (i.e. -2), but the second one won’t…

x <- -5+3   # gives -2
x< -5+3     # gives FALSE!
[1] FALSE

You can start new lines in the middle of commands too, which sometimes makes for cleaner + easier to read code (note the auto-indenting of RStudio)

theWords <-
  c("theFirstWord","theSecondWord","theThirdWord","theFourthWord",
    "theFifthWord","theSixthWord","theSeventhWord")

But again, be careful! If we try the following, we will get an error.

## theWords
##   <-   c("theFirstWord","theSecondWord","theThirdWord","theFourthWord",
##        "theFifthWord","theSixthWord","theSeventhWord")
## 
## Error: unexpected ',' in "       "theFifthWord","

This next command won’t throw an error, but will insert a newline and tab char into the third element, which we wanted to be theThirdWord

theWords <- c("theFirstWord","theSecondWord","theThird
              Word","theFourthWord",
              "theFifthWord","theSixthWord","theSeventhWord")

But instead gets interpreted as:

theWords[3]

It is often useful to organize code into blocks, which you can think of as “paragraphs” – groups of sentences/commands that mean/do a single thing. Also good practice to put comments before every block, or even multiple times within a single block.

wordInfo
  word freq rank
1   w1   10    3
2   w2    4    4
3   w3    3    5
4   w4   12    1
5   w5   12    1

Aaaaand now it’s time to clean up. This is not necessary, but can be comforting if you are a neurotic person :p

rm(x); rm(theWords); rm(wordList); rm(wordFreq); rm(wordRank); rm(wordInfo)

That’s all for now, folks! Shoot me an email if you want to talk/learn/hear more!

# want to get inline syntax highlighting
library(highr)
LS0tCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgo8YnI+PGJyPgoKIyBBIGdlbnRsZSBpbnRybyB0byBwcm9ncmFtbWluZyBhbmQgZGF0YSBhbmFseXNpcyB1c2luZyBSCgoKPCEtLSBXZWxjb21lIHRvIHlvdXIgZmlyc3QgUiBzY3JpcHQhIEFuIFIgc2NyaXB0IGlzIGp1c3QgYSB0ZXh0IGZpbGUgd2l0aCBleHRlbnNpb24gIi5SIiBvciAiLnIiLiBSb3VnaGx5IHNwZWFraW5nLCBhICJzY3JpcHQiIGlzIGJhc2ljYWxseSBqdXN0IGEgInByb2dyYW0uIiAgLS0+CgpUaGlzIGlzIGEgYnJvd3Nlci1mcmllbmRseSB2ZXJzaW9uIG9mIHRoZSBSIHNjcmlwdCB3ZSB1c2VkIGR1cmluZyB0aGUgdHV0b3JpYWwgc2Vzc2lvbi4gWW91IGNhbiBkb3dubG9hZCB0aGUgUiBNYXJrZG93biBmaWxlIHRoYXQgY29udGFpbnMgYWxsIHRoZSBjb2RlIGluIHRoaXMgZG9jdW1lbnQgYnkgc2VsZWN0aW5nICJkb3dubG9hZCBSbWQiIGluIHRoZSBkcm9wZG93biBhdCB0aGUgdG9wIHJpZ2h0IG9mIHRoaXMgcGFnZS4gT3IsIHlvdSBjYW4gZm9sbG93IGFsb25nIGJ5IHR5cGluZyB0aGUgY29tbWFuZHMgaW50byBhIC5SIGZpbGUgZnJvbSB3aXRoaW4gYSBHVUkgbGlrZSBSIFN0dWRpbyAodGhlIG9uZSBJJ2QgcmVjb21tZW5kKS4KCiMjIFNlY3Rpb24gMTogCgpSIHdvcmtzIHdpdGggYW4gaW50ZXJwcmV0ZXIsIHdoaWNoIG1lYW5zIHRoYXQgeW91IGNhbiAqKnJ1bioqLCBha2EgKipleGVjdXRlKiosIAphIHNpbmdsZSAqKmNvbW1hbmQqKiBhdCBhIHRpbWUgKGUuZy4gZnJvbSB3aXRoaW4gUiBTdHVkaW8pLiBBIGNvbW1hbmQgaXMganVzdCBhICoqbGluZSoqIG9yICoqYmxvY2sqKiBvZiBhY3R1YWwgY29kZSAtLSBpbiBvdGhlciB3b3JkcywgCmEgY29tbWFuZCBpcyBqdXN0IGEgbGluZSBvciBibG9jayBvZiBhIHNjcmlwdCB0aGF0IGRvZXNuJ3Qgc3RhcnQgd2l0aCB0aGUgY2hhcmFjdGVyIGAjYC4gClRvIHJ1biBhIGNvbW1hbmQgaXMgdG8gc2VuZCBpdCB0byB0aGUgaW50ZXJwcmV0ZXIuCgpMaW5lcyB0aGF0IHN0YXJ0IHdpdGggYCNgIGFyZSBjYWxsZWQgKipjb21tZW50cyoqLCBhbmQgYXJlIGludGVuZGVkIHRvIGJlIAppbnRlcnByZXRlZCBieSBvdGhlciBodW1hbnMgKG9yIHlvdXJzZWxmIGF0IGEgbGF0ZXIgdGltZSkgLS0gY29tbWVudHMgYXJlIAppZ25vcmVkIGJ5IHRoZSBpbnRlcnBlcnRlciBpZiB5b3UgcnVuIHRoZSBlbnRpcmUgc2NyaXB0IGFsbCBhdCBvbmNlLgoKPCEtLSBZb3UnbGwgbm90aWNlIHRoYXQgSSBzdGFydCBhIG5ldyBsaW5lIGJlZm9yZSB0aGUgdGV4dCByZWFjaGVzIDgwIGNoYXJhY3RlcnMgLS0+CjwhLS0gdGhhdCBpcyBqdXN0IGZvciBnb29kIGNvZGluZyBldGlxdWV0dGUgLS0gd2UgY291bGQgY29udGludWUgYSBsaW5lIGluZGVmaW5pdGVseSBhbmQgbm90aGluZyBiYWQgd291bGQgaGFwcGVuLCBleGNlcHQgdGhhdCB0aGUgbGluZSBzdGFydHMgd3JhcHBpbmcgYW5kIHRoZSBzY3JpcHQgaXMgbm90IGFzIGVhc3kgdG8gbmF2aWdhdGUuIHRoaXMgaXMgdGhlIG9ubHkgbGluZSB0aGF0IHdlIHdpbGwgYWxsb3cgdG8gZXh0ZW5kIGJleW9uZCA4MCBjaGFyYWN0ZXJzIChldmVuIHRoaXMgaXMgbWFraW5nIG1lICpzaHVkZGVyKikgLS0+CgpXaGVuIHlvdSBydW4gYSBjb21tYW5kLCB0aGUgb3V0cHV0IChpZiB0aGVyZSdzIGFueSkgd2lsbCBhcHBlYXIgaW4gdGhlICoqY29uc29sZSoqLCBha2EgKipjb21tYW5kIGxpbmUqKiwgd2hpY2ggeW91IGNhbiBzZWUgYmVsb3cgaW4gUlN0dWRpby4KCk5vdyB3ZSdsbCBzdGFydCBhIG5ldyAqKnNlY3Rpb24qKiwgd2hpY2ggaXMgbm90IG5lY2Vzc2FyeSBidXQgaXMgZ29vZCBwcmFjdGljZS4KPCEtLSB3ZSBqdXN0IHZpc3VhbGx5IHNlcGFyYXRlIGl0IHVzaW5nIGEgYnVuY2ggb2YgbWVhbmluZ2xlc3MgY2hhcmFjdGVycyBsaWtlICc9JyAtLT4KCgojIyBTZWN0aW9uIDE6IG91ciBmaXJzdCBjb21tYW5kcywgYW5kIGxvYWRpbmcvaW5zdGFsbGluZyBwYWNrYWdlcwoKTGV0J3Mgc3RhcnQgd2l0aCBzb21lIGJhc2ljIGNvbW1hbmRzLiAKCkhlcmUgaXMgb3VyIGZpcnN0IGNvbW1hbmQgLS0gZXhlY3V0ZSBpdCB3aXRoICoqY21kK0VudGVyKiogb24gTWFjLCBvciAKKipjdHJsK0VudGVyKiogb24gUEMuIEluIHRoZSB3ZWItdmVyc2lvbiwgdGhlIG91dHB1dCB3aWxsIGFwcGVhciBpbiBhIHNlcGFyYXRlIGJveCBqdXN0IGJlbG93IHRoZSBjb21tYW5kLgoKYGBge3J9CjMgKyA1CmBgYAoKVGhlIG91dHB1dCBib3ggbGluZSBpbmRpY2F0ZXMgdGhhdCB0aGUgZmlyc3QgKGFuZCBvbmx5KSBlbGVtZW50IGAiWzFdImAgb2YgdGhlIG91dHB1dCBpcyBgOGAsIApzbyB0aGUgaW50ZXJwcmV0ZXIgKipyZXR1cm5lZCoqIHRoZSBudW1iZXIgYDhgIGdpdmVuIHRoZSBjb21tYW5kIGA1KzNgLiBUaGF0IHNlZW1zIGJhc2ljYWxseSByaWdodC4gTmljZS4KCldlIGNhbiBkbyBhbGwga2luZHMgb2YgbWF0aGVtYXRpY2FsIGNhbGN1bGF0aW9ucywgZS5nLgoKYGBge3J9CjMgKyAoKDEgLSAoNSAqIDIpKSAvICgzIF4gMikpCmBgYAoKCkluIFIsIGArYCwgYC1gLCBgL2AsIGAqYCwgYW5kIGBeYCBhcmUgKipmdW5jdGlvbnMqKiAob3BlcmF0b3JzLCB0ZWNobmljYWxseSkgLS0gdGhlc2UgZnVuY3Rpb25zIHRha2UKdHdvICoqYXJndW1lbnRzKiogd2hpY2ggYXJlIG51bWJlcnMsIGFuZCByZXR1cm4gYSB0aGlyZCBudW1iZXIgLS0gdGhlICoqdmFsdWUqKiBvZiB0aGUgZnVuY3Rpb24gYXBwbGllZCB0byB0aGUgdHdvIGFyZ3VtZW50cy4KCkJ1dCBtb3N0IGZ1bmN0aW9ucyBpbiBSIGhhdmUgYSBkaWZmZXJlbnQgc3ludGF4IC0tIHRoZXkgdGFrZSB0aGVpciBhcmd1bWVudHMgaW4KYSBtb3JlIGZ1bmN0aW9uLXkgbm90YXRpb24gKCdtZW1iZXIgImYoeCkiIGZyb20gaGlnaCBzY2hvb2wgbWF0aD8pLiBmb3IgZXhhbXBsZSBgc3FydCgpYCB0YWtlcyBvbmUgYXJndW1lbnQgKGEgbnVtYmVyKSBhbmQgCnJldHVybnMgdGhlIHNxdWFyZSByb290IG9mIHRoYXQgYXJndW1lbnQ6CgpgYGB7cn0Kc3FydCg5KSAKYGBgCgoKVGhlIGZ1bmN0aW9ucyBgbWF4KClgIGFuZCBgbWluKClgIGNhbiB0YWtlIGFueSBudW1iZXIgb2YgYXJndW1lbnRzLCBhbmQgcmV0dXJuIHRoZSBoaWdoZXN0L2xvd2VzdCBvZiB0aGVtOgoKYGBge3J9Cm1heCgxLCAyLCAzLCA1LCA0LCA1LCA2LCA2LCAtMTApCm1pbigxLCAyLCAzLCA1LCA0LCA1LCA2LCA2LCAtMTApCmBgYAoKCllvdSBjYW4gYWx3YXlzIHB1bGwgdXAgdGhlIGhlbHAgcGFnZSBmb3IgYSBmdW5jdGlvbiBieSBzYXlpbmcgYD88ZnVuY3Rpb24tbmFtZT4oKWAuIHlvdSBjYW4gc2VhcmNoIGhlbHAgcGFnZXMgd2l0aCBgPz88dG9waWM+YAoKYGBge3J9Cj9tYXgoKQo/P2xpbmd1aXN0aWNzCmBgYAoKCk9uZSBvZiB0aGUgYmVzdCB0aGluZ3MgYWJvdXQgUiBpcyB0aGF0IGFueW9uZSBjYW4gY29udHJpYnV0ZSBoZWxmcHVsLCAKTmV3IHVzZXItZGVmaW5lZCBmdW5jdGlvbnMgYW5kIG1ldGhvZHMgYXJlIGJ1bmRsZWQgaW4gKipwYWNrYWdlcyoqIC0tICBjb2xsZWN0aW9ucyBvZiBmdW5jdGlvbnMgYW5kIG90aGVyIHN0dWZmIHRoYXQgYXJlbid0IHBhcnQgCm9mIHdoYXQgYXV0b21hdGljYWxseSBjb21lcyB3aXRoIFIgd2hlbiB5b3UgaW5zdGFsbCBpdCAoYWthICoqYmFzZSBSKiopLgoKWW91IHNob3VsZCBjaGVjayBvdXQgdGhlIGBsYW5ndWFnZVJgIHBhY2thZ2UsIHdoaWNoIGlzIGEgY29tcGFuaW9uIHRvIHRoZSAKZXhjZWxsZW50IDIwMDcgdGV4dGJvb2sgYnkgSGFyYWxkIEJhYXllbiwKKkFuYWx5emluZyBMaW5ndWlzdGljIERhdGE6IEEgUHJhY3RpY2FsIEludHJvZHVjdGlvbiB0byBTdGF0aXN0aWNzKi4gCihwc2E6IHlvdSBqdXV1dXN0IG1pZ2h0IGJlIGFibGUgdG8gZmluZCBhIGJvb3RsZWcgdmVyc2lvbiBpZiB5b3UgbG9vayBvbmxpbmUpCgoKSWYgeW91IGFyZSB1c2luZyBhIHBhcnRpY3VsYXIgcGFja2FnZSBmb3IgdGhlIGZpcnN0IHRpbWUsIHlvdSB3aWxsCmhhdmUgdG8gaW5zdGFsbCBpdCwgd2hpY2ggY2FuIGJlIGRvbmUgd2l0aCBgaW5zdGFsbC5wYWNrYWdlcygiPHBhY2thZ2UgbmFtZT4iKWAKbm90ZSBxdW90ZXMgYXJvdW5kIHRoZSBuYW1lLCB3aGljaCBhcmUgbmVjZXNzYXJ5IHdoZW4gaW5zdGFsbGluZwoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygibGFuZ3VhZ2VSIikKYGBgCgoKQWZ0ZXIgYSBwYWNrYWdlIGlzIGluc3RhbGxlZCwgeW91IGxvYWQgaXQgd2l0aCBgbGlicmFyeSgpYAoKYGBge3J9CmxpYnJhcnkoImxhbmd1YWdlUiIpIApgYGAKCgpZb3UgY2FuIHNlZSB5b3VyIGxpYnJhcnkgLS0gYSBsaXN0IG9mIHlvdXIgKippbnN0YWxsZWQqKiBwYWNrYWdlcyAtLSBieSBzYXlpbmcKYGxpYnJhcnkoKWAsICp3aXRob3V0KiBhbiBhcmd1bWVudC4gWW91IGNhbiBzZWUgd2hpY2ggcGFja2FnZXMgYXJlIGN1cnJlbnRseSAqKmxvYWRlZCoqIHdpdGgKYHNlYXJjaCgpYCwgYWdhaW4gd2l0aCBubyBhcmd1bWVudC4gV2Ugd2lsbCB1c2Ugc29tZSBmdW5jdGlvbnMgZnJvbSBub24tYmFzZSBwYWNrYWdlcyBpbiBzZWN0aW9uIDYgYmVsb3cKCgojIyBTZWN0aW9uIDI6IHZhcmlhYmxlcyBhbmQgYXNzaWdubWVudHMKCgpNb3N0IG9mIHRoZSB0aW1lIHdlIGRvbid0IGp1c3Qgd2FudCB0byBjYWxjdWxhdGUgc29tZXRoaW5nLCBidXQgd2Ugd2FudCB0byAKY2FsY3VsYXRlIHNvbWV0aGluZyBhbmQgKipzdG9yZSoqIHRoZSByZXN1bHQgZm9yIGxhdGVyIHVzZS4gV2UgZG8gdGhpcyBieSAqKmFzc2lnbmluZyoqIHRoZSByZXN1bHQgb2YgYSBjb21tYW5kIHRvIGEgKip2YXJpYWJsZSoqCnN1Y2ggYXMgYHhgIGJ5IHVzaW5nIHRoZSAqKmFzc2lnbm1lbnQqKiBvcGVyYXRvciBgPWAgb3IgYDwtYAoKYGBge3J9CnggPSA1ICsgMwpgYGAKCkl0IGlzIHNhZmVyIHRvIHVzZSBgPC1gIGZvciBhc3NpZ25tZW50IHRoYW4gYD1gLCBidXQgSSB0aGluayBgPWAgaXMgbW9yZQppbnR1aXRpdmUsIHNvIHdlIHdpbGwgdXNlIGl0IGhlcmUgKHNlZSBzZWMgNyBmb3IgZGlzY3Vzc2lvbikuCgoKTm90aWNlIGluIHRoZSB1cHBlciByaWdodCBwYW5lIG9mIFJTdHVkaW8sIHlvdSBjYW4gbm93IHNlZSBgeGAgaXMgc2F2ZWQuIFdlIGNhbiBsb29rIGF0IHRoZSAqKnZhbHVlKiogb2YgdGhlIHZhcmlhYmxlIGB4YCAKYnkgc2ltcGx5IHNlbmRpbmcgYHhgIHRvIHRoZSBpbnRlcnByZXRlcgoKYGBge3J9CngKYGBgCgoKV2UgY2FuIHN0b3JlIHRoZSByZXN1bHRzIG9mIGJhc2ljYWxseSBhbnl0aGluZyBpbiBhIHZhcmlhYmxlLCBhbmQgCndlIGNhbiAoKmFuZCBzaG91bGQqKSB1c2UgdXNlZnVsIG5hbWVzIGZvciB2YXJpYWJsZXMgCgpgYGB7cn0KYmlnZ2VzdCA8LSBtYXgoMSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSwgMTApCmJpZ2dlc3QKYGBgCgoKTWFrZSBzdXJlIHlvdXIgdmFyaWFibGUgbmFtZXMgZG9uJ3QgY29udGFpbiBtYXRoIG9wZXJhdG9ycyBsaWtlIGAtYCBvciBgK2AsIAphbmQgbWFrZSBzdXJlIHRoZXkgZG9uJ3Qgc3RhcnQgd2l0aCBhIG51bWJlcjogd2UgY2FuIG5hbWUgYSB2YXIgJ3ZhcjEnCgpgYGB7cn0KdmFyMSA9IDEgKyAxCmBgYAoKCkJ1dCBub3QgYDFzdFZhcmAsIGJjIGl0IHN0YXJ0cyB3aXRoIGEgbnVtYmVyOgoKYGBge3J9CiMgaWYgd2UgdHJ5OiAgICAxc3RWYXIgPSAxICsgMQojIHdlIHdpbGwgZ2V0OiAgRXJyb3I6IHVuZXhwZWN0ZWQgc3ltYm9sIGluICIxc3RWYXIiCmBgYAoKRXJyb3JzIGFuZCBlcnJvciBtZXNzYWdlcyBhcmUgYW5ub3lpbmcgYnV0IGV4dHJlbWVseSBpbXBvcnRhbnQgKyB1c2VmdWwhIAoKVG8gYXZvaWQgY2x1dHRlciwgd2UgY2FuIHJlbW92ZSB2YXJzIGZyb20gdGhlIHdvcmtzcGFjZSB3aGVuIHdlIGFyZSBkb25lIHdvcmtpbmcgdyB0aGVtCgpgYGB7cn0Kcm0odmFyMSkKcm0oYmlnZ2VzdCkKcm0oeCkKYGBgCgoKCkEgY29tbW9uIHBpdGZhbGwgaXMgdG8gY29uZnVzZSBgPWAgKGFzc2lnbm1lbnQsIGFyZ3VtZW50IGJpbmRpbmcpIHdpdGggYD09YCAoZXF1YWxzKS4KCmBgYHtyLCBlY2hvPVRSVUV9CiMjaWYgd2UgdHJ5OiAgICA1ICsgMyA9IDgKIyN3ZSB3aWxsIGdldDogIAojIyBFcnJvciBpbiA1ICsgMyA9IDggOiB0YXJnZXQgb2YgYXNzaWdubWVudCBleHBhbmRzIHRvIG5vbi1sYW5ndWFnZSBvYmplY3QKIyAxIDwtIDEKYGBgCgoKVG8gc2F5IHdoYXQgd2UgbWVhbnQsIHdlIHdvdWxkIHVzZSBgPT1gCgpgYGB7cn0KNSArIDMgPT0gOApgYGAKCgoKCiMjIFNlY3Rpb24gMzogZGF0YSB0eXBlcyBhbmQgZGF0YSBzdHJ1Y3R1cmVzIAoKCldlJ3ZlIGRvbmUgc29tZSBiYXNpYyBtYXRoLCB3aGljaCBpcyBiYXNpY2FsbHkgbWFuaXB1bGF0aW5nICoqbnVtYmVycyoqLiBCdXQgdXN1YWxseSB3ZSB3YW50IHRvIGRvIGEgbG90IG1vcmUgdGhhbiB0aGF0IC0tIAp3ZSB3YW50IHRvIG1hbmlwdWxhdGUgY2hhcmFjdGVyIHN0cmluZ3MgKGUuZy4gd29yZHMpIHRvby4gClRoaXMgaXMgbWFkZSBwb3NzaWJsZSBieSB0aGUgZXhpc3RlbmNlIG9mIGRpZmZlcmVudCAqKmRhdGEgdHlwZXMqKi4gV2UgY2FuIHNlZSB0aGUgdHlwZSBvZiBhbiBvYmplY3QgYnkgdXNpbmcKCmBgYHtyfQp4ID0gNQpjbGFzcyh4KQpgYGAKCmxldCdzIGdpdmUgeCBhIG5ldyB2YWx1ZSArIHNlZSBpdHMgdHlwZSAoY2hhcmFjdGVyIHN0cmluZykKCmBgYHtyfQp4ID0gImJvb3NoIgpjbGFzcyh4KQpgYGAKCgpgbG9naWNhbGAgKHRydWUvZmFsc2U7IGJpbmFyeSkgaXMgYWxzbyBhIGJhc2ljIGRhdGEgdHlwZToKCmBgYHtyfQp4ID0gVFJVRQpjbGFzcyh4KQpgYGAKCgpVc3VhbGx5IHdlIHdvcmsgd2l0aCBtb3JlIGNvbXBsaWNhdGVkICoqZGF0YSBzdHJ1Y3R1cmVzKiogdGhhbiBzdHJpbmdzIG9yIG51bWJlcnMuCgpgYGB7cn0KIyBsaWtlIHZlY3RvcnMKdmVjMSA9IGMoImJvb3NoMSIsImJvb3NoMiIsImJvb3NoMyIpCnZlYzEKYGBgCgpgYGB7cn0KIyBhbmQgbGlzdHMKbGlzMSA9IGxpc3QoImJvb3NoMSIsImJvb3NoMiIsImJvb3NoMyIpCmxpczEKYGBgCgpgYGB7cn0KIyB3aGljaCBhcmUgKm5vdCogdGhlIHNhbWUgdGhpbmcKaWRlbnRpY2FsKHZlYzEsbGlzMSkKYGBgCgoKCkJ1dCB0aGUgbW9zdCBpbXBvcnRhbnQgZGF0YSBzdHJ1Y3R1cmUgaW4gUiBpcyB0aGUgKipkYXRhIGZyYW1lKioKCmBgYHtyfQpkZiA9IGRhdGEuZnJhbWUoCiAgY29sMT1sZXR0ZXJzWzE6Nl0sICAgICAgICAgICAgICAgICAgICAgICMgZ2l2ZXMgdXMgYygiYSIsImIiLCJjIiwiZCIsImUiLCJmIikKICBjb2wyPTE6NiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzYW1lIGFzIGMoMSwyLDMsNCw1LDYpCiAgY29sMz1yZXAoYygiZ3JvdXAxIiwiZ3JvdXAyIiksIHRpbWVzPTMpICMgcmVwZWF0ICJncm91cDEiLCJncm91cDIiIHRocmljZQopCmBgYAoKCk5vdGUgdGhhdCBhcmd1bWVudCBwbGFjZXMgaGF2ZSAqKm5hbWVzKiosIGUuZy4gdGhlIGZ1bmN0aW9uIGByZXAoKWAgaGFzIGFuIGFyZ3VtZW50IGNhbGxlZCBgdGltZXNgLCB3aGljaCBpbmRpY2F0ZXMgdGhlIG51bWJlciBvZiB0aW1lcyB0aGF0IHRoZSBmaXJzdCBhcmd1bWVudCB0byBgcmVwKClgIHNob3VsZCBiZSByZXBlYXRlZC4gV2Ugc2F0dXJhdGVkIGB0aW1lc2Agd2l0aCBgM2AgYWJvdmUsIHlpZWxkaW5nIGEgdmVjdG9yIG9mIGxlbmd0aCBgNmAuCgpBIGBkYXRhLmZyYW1lKClgIGlzIGZvciB0aGUgbW9zdCBwYXJ0IGp1c3QgYSBsaXN0IG9mIHZlY3RvcnMgYHYxYCxgdjJgLC4uLixgdm5gIHN1Y2ggdGhhdApgbGVuZ3RoKHYxKT09bGVuZ3RoKHYyKT09YC4uLmA9PWxlbmd0aCh2bilgCgpgYGB7cn0KaXMubGlzdChkZikKaXMuZGF0YS5mcmFtZShkZikKYGBgCgoKQnV0IHlvdSBzaG91bGQgdGhpbmsgb2YgYSBkYXRhZnJhbWUgYXMgYSBzcHJlYWRzaGVldCB0aGF0IGNvbnRhaW5zICoqZGF0YSoqLAp1c3VhbGx5IHdoZXJlIGVhY2ggcm93IGlzIGEgc2VxdWVuY2Ugb2YgZmFjdHMgYWJvdXQgYSBzaW5nbGUgdGhpbmcKCmBgYHtyfQpkZgpgYGAKCgoKTm93IGxldCdzIGNsZWFuIHVwIHRoZSB3b3Jrc3BhY2UgYmVmb3JlIG1vdmluZyBvbiAKKGRvaW5nIHRoaXMgc2F2ZXMgbWVtb3J5ICsgY2FuIGhlbHAga2VlcCB5b3VyIGFnZW5kYSBjbGVhcikuIEluc3RlYWQgb2Ygc3RhcnRpbmcgYSBuZXcgbGluZSwgd2UgY2FuIGluZGljYXRlIHRoZSBlbmQgb2YgYSBjb21tYW5kIHdpdGggYDtgCgpgYGB7cn0Kcm0oZGYpOyBybShsaXMxKTsgcm0odmVjMSk7IHJtKHgpCmBgYAoKCgojIyBTZWN0aW9uIDQ6IERhdGEgZnJhbWVzIGFuZCByZWFkaW5nIGZpbGVzIAoKTGV0J3MgY2hlY2sgb3V0IHRoZSBmcmVxdWVuY3kgbGlzdCB3ZSB1c2VkIGZvciBob21ld29yayAjNC4gV2UnbGwgcmVhZCBpdCBpbiB3aXRoIGByZWFkLmNzdigpYCBhbmQgc3RvcmUgaXQgaW4gYSB2YXIgY2FsbGVkIGB3b3Jkc2AuCgpgYGB7cn0Kd29yZHMgPC0gcmVhZC5jc3YoZmlsZT0idG9wNWstd29yZC1mcmVxdWVuY3ktZG90LWluZm8uY3N2IiwKICAgICAgICAgICAgICAgICAgc2VwPSIsIiwgaGVhZGVyPVRSVUUpCmBgYAoKYHJlYWQuY3N2KClgIGdpdmVzIHVzIGEgZGF0YSBmcmFtZSAoaGVuY2Vmb3J0aCBkZikuCgpgYGB7cn0KY2xhc3Mod29yZHMpCmBgYAoKClRoZSBmaXJzdCB0aGluZyB3ZSBzaG91bGQgZG8gd2hlbiByZWFkaW5nIGluIGEgbmV3IGRmIGlzIGNoZWNrIGl0IG91dAoKYGBge3J9CiMgc2VlIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRmIC0tIGluZm8gb24gZWFjaCBjb2x1bW4Kc3RyKHdvcmRzKQojIGdldCBhIHN1bW1hcnkgb2YgZWFjaCBjb2wgb2YgdGhlIGRmCnN1bW1hcnkod29yZHMpIApgYGAKCgp3ZSBjYW4gYWxzbyBsb29rIGF0IHRoZSBmaXJzdCBuIHJvd3MgYnkgY2FsbGluZyBoZWFkKCkgYW5kIHNwZWNpZnlpbmcgbgoKYGBge3J9CmhlYWQod29yZHMsIG49NSkKYGBgCgoKKmluIFJTdHVkaW8gb25seSogd2UgY2FuIHVzZSBgVmlldygpYCB0byBzZWUgdGhlIHdob2xlIHRoaW5nIGluIGEgd2luZG93CihjYW4gYWxzbyBjbGljayBvbiB0aGUgZGYgaW4gdGhlIHVwcGVyIHJpZ2h0aGFuZCBwYW5lKQoKYGBge3J9ClZpZXcod29yZHMpCmBgYAoKCgojIyBTZWN0aW9uIDU6IEJhc2ljIG9wZXJhdGlvbnMgb24gZGF0YSBmcmFtZXMgCgpUaGUgaW5kaXZpZHVhbCBjb2xzIG9mIGEgZGYgYXJlIHJlZmVycmVkIHRvICJ2YXJpYWJsZXMiIChjb25mdXNpbmcsIGlrISkKYW5kIHRoZXkgY2FuIGJlIGFjY2Vzc2VkIGJ5IG5hbWUgd2l0aCB0aGUgYCRgIG9wZXJhdG9yCgpgYGB7cn0Kd29yZENvbCA8LSB3b3JkcyRXb3JkCmBgYAoKCkp1c3QgdmlldyB0aGUgZmlyc3QgMTAgZWxlbWVudHMgb2YgYSB2ZWN0b3Igd2l0aCBgPHZlYy1uYW1lPlsxOjEwXWAKCmBgYHtyfQp3b3JkQ29sWzE6MTBdCnJtKHdvcmRDb2wpCmBgYAoKV2UgY2FuIGFjY2VzcyBhIHJvdyBvciByYW5nZSBvZiByb3dzIHVzaW5nIHRoZSBgWyAsIF1gIG9wZXJhdG9yIChmb3Igcm93cywgYmVmb3JlIHRoZSBjb21tYSkKCmBgYHtyfQp3b3Jkc1sxOjIsXQpgYGAKCgpBbHNvIHdvcmtzIGZvciBjb2xzIChhZnRlciB0aGUgY29tbWEpCgpgYGB7cn0KaGVhZCh3b3Jkc1ssMToyXSwgbj01KQpgYGAKCgpXZSBjYW4gYWRkIG5ldyBjb2xzIGJ5IGNvbWJpbmluZyBgJGAgd2l0aCB0aGUgYXNzaWdubWVudCBvcGVyYXRvcgoKYGBge3J9CndvcmRzJG5ld0NvbCA8LSB3b3JkcyRGcmVxdWVuY3kgLyB3b3JkcyRSYW5rCmhlYWQod29yZHMsIG49NSkKCmBgYAoKCgp3ZSBjYW4gcmVtb3ZlIGNvbHMgd2Ugbm8gbG9uZ2VyIG5lZWQgb3IgZG9uJ3Qgd2FudCBieSBhc3NpZ25pbmcgdGhlbSB0byBgTlVMTGAKCgpgYGB7cn0Kd29yZHMkbmV3Q29sIDwtIE5VTEwKYGBgCgoKV2UgY2FuIGFsc28gY2hhbmdlIHRoZSBkYXRhIHR5cGUgb2YgYSBnaXZlbiBjb2wuIApgd29yZHMkUGFydE9mU3BlZWNoYCBpcyBhICoqZmFjdG9yKiosIG1lYW5pbmcgYSBkaXNjcmV0ZSBjYXRlZ29yeSB3aXRoIAphIHNwZWNpZmllZCBzZXQgb2YgcG9zc2libGUgdmFsdWVzIC0tIGkuZS4gKipsZXZlbHMqKgoKYGBge3J9CmNsYXNzKHdvcmRzJFBhcnRPZlNwZWVjaCkKbGV2ZWxzKHdvcmRzJFBhcnRPZlNwZWVjaCkKYGBgCgoKV2UgY291bGQgY2hhbmdlIHAuby5zLiB0byBjaGFyYWN0ZXIgdHlwZSB3aXRoCgpgYGB7cn0Kd29yZHMkUGFydE9mU3BlZWNoIDwtIGFzLmNoYXJhY3Rlcih3b3JkcyRQYXJ0T2ZTcGVlY2gpCmBgYAoKCmBgYHtyfQpjbGFzcyh3b3JkcyRQYXJ0T2ZTcGVlY2gpCmBgYAoKCkJ1dCB3ZSBzaG91bGQgY2hhbmdlIGl0IGJhY2sgYmVjYXVzZSBwLm8ucy4gYWN0dWFsbHkgaXMgYSBmYWN0b3IKYGBge3J9CndvcmRzJFBhcnRPZlNwZWVjaCA8LSBhcy5mYWN0b3Iod29yZHMkUGFydE9mU3BlZWNoKSAKYGBgCgoKCkl0IGlzIG9mdGVuIHVzZWZ1bCB0byAqKnN1YnNldCoqIGEgZGYgaWYgd2UgYXJlIGludGVyZXN0ZWQgaW4gb25seSAKc29tZSBwYXJ0IG9mIGl0IC0tIHVzdWFsbHkgc29tZSBzdWJzZXQgb2YgdGhlIHJvd3MuIFN1cHBvc2Ugd2Ugb25seSB3YW50IHRvIGxvb2sgYXQgaW5mbyBhYm91dCB0aGUgMTAwIG1vc3QgZnJlcXVlbnQgd29yZHMuIFdlIGNvdWxkIGNhcnZlIG91dCB0aGUgdG9wIDEwMCBieSBzYXlpbmcgYW55IG9mIHRoZSBmb2xsb3dpbmc6CgpgYGB7cn0KZnJlcXVlbnRXb3JkcyA8LSBzdWJzZXQod29yZHMsIFJhbmsgJWluJSAxOjEwMCkKZnJlcXVlbnRXb3JkcyA8LSBzdWJzZXQod29yZHMsIFJhbmsgPCAxMDEpCmZyZXF1ZW50V29yZHMgPC0gc3Vic2V0KHdvcmRzLCBSYW5rIDw9IDEwMCkKYGBgCgoKVGhlcmUgYXJlIHNldmVyYWwgZnVuY3Rpb25zIHRoYXQgY2FuIGJlIHVzZWQgZm9yIHN1YnNldHRpbmcKd2UgY2FuIGFsc28gdXNlIGEgbW9yZSBwcmltaXRpdmUgKGJ1dCBzYWZlciArIHVsdGltYXRlbHkgYmV0dGVyKSB3YXk6CgpgYGB7cn0KZnJlcXVlbnRXb3JkcyA8LSB3b3Jkc1t3b3JkcyRSYW5rICVpbiUgMToxMDAsIF0KZnJlcXVlbnRXb3JkcyA8LSB3b3Jkc1t3b3JkcyRSYW5rIDwgMTAxLCBdCmZyZXF1ZW50V29yZHMgPC0gd29yZHNbd29yZHMkUmFuayA8PSAxMDAsIF0Kcm0oZnJlcXVlbnRXb3JkcykKCmBgYAoKCllvdSBjYW4gc2VlIHRoYXQgdGhlc2UgbWV0aG9kcyB5aWVsZCB0aGUgc2FtZSByZXN1bHQ6CgpgYGB7cn0KaWRlbnRpY2FsKHN1YnNldCh3b3JkcywgUmFuayA8IDEwMSksIHdvcmRzW3dvcmRzJFJhbmsgPCAxMDEsIF0pCmBgYAoKCk5vdyBzdXBwb3NlIHdlIG9ubHkgd2FudCB0byBsb29rIGF0IG5vdW5zLiB0aGVuIHdlIGNvdWxkIGRlZmluZSBgbm91bnNgIGFzCgpgYGB7cn0Kbm91bnMgPC0gc3Vic2V0KHdvcmRzLCBQYXJ0T2ZTcGVlY2g9PSJuIikKYGBgCgoKSGVyZSBpcyBhICoqdHJpY2t5KiogZmFjdDogd2hlbiB5b3Ugc3Vic2V0IGEgZGYsIHlvdSByZXRhaW4gYWxsCm9yaWdpbmFsIGZhY3RvciBsZXZlbHMsIGV2ZW4gaWYgdGhleSBhcmVuJ3QgaW5zdGFudGlhdGVkIGluIHRoZSBzdWJzZXQKCmBgYHtyfQpsZXZlbHMobm91bnMkUGFydE9mU3BlZWNoKQpsZXZlbHMobm91bnMkV29yZClbMToxMF0KYGBgCgoKV2UgdXN1YWxseSB3YW50IHRvIGVsaW1pbmF0ZSBmYWN0b3IgbGV2ZWxzIHRoYXQgZG8gbm90IG9jY3VyIGluIHRoZSBzdWJzZXQ6CgpgYGB7cn0Kbm91bnMgPC0gZHJvcGxldmVscyhub3VucykKYGBgCgoKWW91IGNhbiBhbHNvIHdyYXAgYHN1YnNldCgpYCB3aXRoIGBkcm9wbGV2ZWxzKClgIGluIHRoZSBzdWJzZXR0aW5nIGNhbGwgaXRzZWxmOgoKYGBge3J9Cm5vdW5zIDwtIGRyb3BsZXZlbHMoc3Vic2V0KHdvcmRzLCBQYXJ0T2ZTcGVlY2g9PSJuIikpCmBgYAoKCgoKIyMgU2VjdGlvbiA2OiBzdW1tYXJ5IHN0YXRpc3RpY3MgYW5kIHZpc3VhbGl6aW5nIGRhdGEKCldlIGNhbiBnZXQgdGhlIGF2ZXJhZ2Ugb2YgYSBudW1lcmljIHZlY3RvciAoaW5jbC4gYSBkZiBjb2x1bW4pIHVzaW5nIGBtZWFuKClgCgpgYGB7cn0KbWVhbih3b3JkcyRSYW5rKQpgYGAKCgpTb21ldGltZXMgZGYncyB3aWxsIGhhdmUgKiptaXNzaW5nIHZhbHVlcyoqLCBpbmRpY2F0ZWQgd2l0aCBgTkFgCmlmIHdlIHRyeSB0byB0YWtlIHRoZSBtZWFuIG9mIGEgdmVjdG9yIGNvbnRhaW5pbmcgYE5BYCwgd2UgZ2V0IGBOQWAgYXMgYSByZXN1bHQsIGV2ZW4gaWYgOTklIG9mIHRoZSB2ZWN0b3IgaGFzIGxlZ2l0aW1hdGUgbnVtYmVycyBpbiBpdC4KCmBgYHtyfQptZWFuKGMoMSwyLDMsTkEpKQpgYGAKCgpTbyBpdCBpcyBvZnRlbiBzYWZlc3QgdG8gc2V0IHRoZSBuYS5ybSBhcmd1bWVudCB0byBgVFJVRWAsIGFzIGluOgoKYGBge3J9Cm1lYW4oYygxLDIsMyxOQSksIG5hLnJtPVRSVUUpCmBgYAoKCldlIGNhbiBhbHNvIHNlZSBtb3JlIGRldGFpbGVkIGluZm8gb2YgYSBjb2wgd2l0aCBgc3VtbWFyeSgpYAoKYGBge3J9CnN1bW1hcnkod29yZHMkRnJlcXVlbmN5KQpgYGAKCgoKV2UgY2FuIGxvb2sgYXQgYSB0YWJsZSBvZiBhIGZhY3RvciBjb2wgb3Igc2VlIGEgc3VtbWFyeQoKYGBge3J9CnRhYmxlKHdvcmRzJFBhcnRPZlNwZWVjaCkKYGBgCgoKCldlIGNhbiBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgYSBudW1lcmljIHZhciB3aXRoIGEgaGlzdG9ncmFtOgoKYGBge3J9Cmhpc3QobG9nKHdvcmRzJEZyZXF1ZW5jeSkpICMgZWFzaWVyIHRvIHNlZSBkaXN0IGlmIHdlIHRha2UgbG9nIGZpcnN0CmBgYAoKCldlIGNhbiBtYWtlIGEgc2NhdHRlcnBsb3Qgb2YgdHdvIG51bWVyaWMgdmFyaWFibGVzLgoKCmBgYHtyfQojIChlYXNpZXIgdG8gc2VlIHcgYSBzdWJzZXQpCm5vdW5zRnJlcSA8LSBzdWJzZXQobm91bnMsIFJhbmsgPCA1MDApCgpwbG90KG5vdW5zRnJlcSRSYW5rLCBub3Vuc0ZyZXEkRnJlcXVlbmN5KQpgYGAKCgpXYW93IGxvb2tzIGxpa2UgYSBzdHJvbmcgcmVsYXRpb25zaGlwLi4uQWN0dWFsbHksIHdlIGFyZSBsb29raW5nIGF0IGEgdmlzdWFsIGV4YW1wbGUgb2YgKipaaXBmJ3MgTGF3KiohISEKCmBgYHtyfQpwbG90KGxvZyhub3Vuc0ZyZXEkUmFuayksIGxvZyhub3Vuc0ZyZXEkRnJlcXVlbmN5KSkKYGBgCgpaaXBmJ3MgTGF3IHN0YXRlcyB0aGF0IHdvcmQtZnJlcXVlbmN5IGlzIGludmVyc2VseSByZWxhdGVkIHRvIGZyZXF1ZW5jeSByYW5rIC0tIHdlIGNhbiBzZWUgdGhhdCB0aGUgbGluZWFyIGNvcnJlbGF0aW9uIGJldHdlZW4gZnJlcXVlbmN5IGFuZCByYW5rIGlzIHZlcnkgc3Ryb25nOyBpZiB3ZSBhcHBseSBhIGxvZyB0cmFuc2Zvcm1hdGlvbiwgdGhlIHJlbGF0aW9uc2hpcCBpcyBhYm91dCBhcyBwZXJmZWN0IGFzIHlvdSB3aWxsIGV2ZXIgc2VlIGluIGFuIGVtcGlyaWNhbCBkb21haW4uCgpgYGB7cn0KY29yKG5vdW5zRnJlcSRSYW5rLCBub3Vuc0ZyZXEkRnJlcXVlbmN5KQpjb3IobG9nKG5vdW5zRnJlcSRSYW5rKSwgbG9nKG5vdW5zRnJlcSRGcmVxdWVuY3kpKQpgYGAKCkVuZCB0YW5nZW50IGludG8gWmlwZidzIGxhdy4KCldlIGNhbiBhbHNvIHBsb3Qgc3Vic2V0cyBvZiBhIG51bWVyaWMgdmFyaWFibGUgZm9yIGVhY2ggbGV2ZWwgb2YgYSBmYWN0b3IKCmBgYHtyfQpwbG90KHdvcmRzJFBhcnRPZlNwZWVjaCwgd29yZHMkUmFuaykKYGBgCgpUaGlzIHBsb3QgaXMgdmVyeSBpbmZvcm1hdGl2ZSBidXQgbm90IHZlcnkgaW50dWl0aXZlIHRvIHJlYWQuIFdoYXQgYWJvdXQgc29tZXRoaW5nIGVhc2llciB0byB1bmRlcnN0YW5kLCBsaWtlIGEgYmFyIGdyYXBoPyBTcGVjaWZpY2FsbHksIHdoYXQgd291bGQgYmUgbmljZSBpcyBpZiBmb3IgZWFjaCBwYXJ0IG9mIHNwZWVjaCwgd2UgaGFkIGEgYmFyIHdob3NlIApoZWlnaHQgaXMgZXF1YWwgdG8gdGhlIGF2ZXJhZ2UgcmFuayBvZiB3b3JkcyB3aXRoIHRoYXQgcGFydCBvZgpzcGVlY2gsIHdpdGhpbiBvdXIgZGF0YXNldC4KClRvIG1ha2UgdGhlIHZpc3VhbGl6YXRpb24gbW9yZSBtZWFuaW5nZnVsLCBsZXQncyBjdXQgdGhlIGRmIHNvIHRoYXQKaXQgY29udGFpbnMgb25seTogbm91bnMsIHZlcmJzLCBhZGplY3RpdmVzLCBwcmVwb3NpdGlvbnMsIGFydGljbGVzLCBhbmQgcHJvbm91bnMuCgoKYGBge3J9Cm52YXAgPC0gc3Vic2V0KHdvcmRzLCBQYXJ0T2ZTcGVlY2ggJWluJSBjKCJuIiwidiIsImoiLCJpIiwiYSIsInAiKSkKYGBgCgpUaGVyZSBhcmUgYSBudW1iZXIgb2Ygd2F5cyB0byBtYWtlIHRoZSBwbG90IHdlIGFyZSBhaW1pbmcgZm9yLiBXZSB3aWxsIGRvIGl0IGluIGEgd2F5IHRoYXQgd2lsbCBpbGx1c3RyYXRlIGhvdyB0byBleGVjdXRlIGFub3RoZXIgaW1wb3J0YW50IGFuZCBjb21tb24gdGFzayBpbiBSOiBhZ2dyZWdhdGlvbi4gV2UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYGFnZ3JlZ2F0ZSgpYCB0byBjYWxjdWxhdGUgdGhlIG1lYW4gcmFuayBmb3IgZWFjaCBzdWJzZXQgb2YgdGhlIGRhdGEgZGVmaW5lZCBieSBhIGxldmVsIG9mIGBudmFwJFBhcnRPZlNwZWVjaGAgKHRoZXJlIGFyZSBvdGhlciB3YXlzIHRvIGRvIHRoaXMgYXMgd2VsbCkuIFRoZSByZXN1bHQgb2YgdGhpcyBgYWdncmVnYXRlKClgIGNhbGwgd2lsbCBiZSBhIGRmIGNvbnRhaW5pbmcgdGhlIGdyb3VwIG1lYW5zOgoKYGBge3J9Cm1lYW5SYW5rcyA8LSBhZ2dyZWdhdGUoUmFuayB+IFBhcnRPZlNwZWVjaCwgRlVOPSJtZWFuIiwgZGF0YT1udmFwKQptZWFuUmFua3MKYGBgCgoKTm93IHRoYXQgd2UgaGF2ZSBvdXIgbWVhbnMsIHdlIGNhbiBtYWtlIGEgcGxvdCBvZiB0aGVtLiBJZiB5b3UgZXZlciBnZXQgbW9yZSBpbnRvIFIsIHlvdSB3aWxsIHdhbnQgdG8gdXNlIAp0aGUgYW1hemluZyB2aXN1YWxpemF0aW9uIGxpYnJhcnkgYGdncGxvdDI6OmAgKGZvciAiZ3JhbW1hciBvZiBncmFwaGljcyIpCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KIyBpbnN0YWxsIGl0IGlmIHlvdSBoYXZlbid0IGFscmVhZHkKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKbGlicmFyeShnZ3Bsb3QyKQpgYGAKCkFuZCBoZXJlJ3MgdGhlIHBsb3QgKEkgd29uJ3QgZXhwbGFpbiBgZ2dwbG90MmAgc3ludGF4IGhlcmUsIGl0IGlzIGEgYml0IGxlc3MgaW50dWl0aXZlIHRoYW4gdGhlIGJhc2UgZ3JhcGhpY3MgY2FwYWJpbGl0aWVzLCBidXQgd2VsbC13b3J0aCBsZWFybmluZykuCgpgYGB7cn0KZ2dwbG90KGRhdGE9bWVhblJhbmtzLCBhZXMoeD1QYXJ0T2ZTcGVlY2gseT1SYW5rKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIGdndGl0bGUoImF2ZXJhZ2UgZnJlcXVlbmN5IHJhbmsgYnkgcGFydCBvZiBzcGVlY2giKQpgYGAKCgpJbnRlcmVzdGluZy4uLldoYXQgZG9lcyB0aGUgcGxvdCB0ZWxsIHVzIGFib3V0IGZyZXF1ZW5jeSBhbmQgcGFydHMgb2Ygc3BlZWNoPwoKCmBgYHtyfQojIHNlYyA2IGNsZWFudXAKcm0obnZhcCk7IHJtKG1lYW5SYW5rcyk7IHJtKG5vdW5zRnJlcSk7IHJtKG5vdW5zKQpgYGAKCiMjIFNlY3Rpb24gNzogc3R1ZmYgdG8ga25vdyBhYm91dCBSIHN5bnRheAoKClVzaW5nIGA9YCBmb3IgdmFyIGFzc2lnbm1lbnQgaXMgYWN0dWFsbHkgbm90IGEgZ29vZCBwcmFjdGljZSAoZm9yIHJlYXNvbnMgeW91CmRvbid0IG5lZWQgdG8gdW5kZXJzdGFuZCBhdCB0aGlzIHBvaW50IC0tIGFzayBtZSBpZiB5b3UncmUgaW50ZXJlc3RlZCkKCldlICpzaG91bGQqIGJlIHVzaW5nIGA8LWAsIHdoaWNoIGlzIHVuZm9ydHVuYXRlIGJjIGl0IGxlc3Mgb2J2aW91cyArIGludHVpdGl2ZS4gVGhlIHJlYXNvbiBpcyB0aGF0LCB3aGlsZSBgPWAgYW5kIGA8LWAgaGF2ZSBzb21lIGNvbW1vbiB1c2VzLCBib3RoIGhhdmUgdXNlcyBzdWNoIHRoYXQsIGlmIHdlIHVzZSB0aGUgb3RoZXIgb25lIGluc3RlYWQsIHRoaW5ncyB3aWxsIGJyZWFrLiBBc2sgbWUgbW9yZSBpZiB5b3UgYXJlIGludGVyZXN0ZWQuCgpgYGB7cn0KeCA8LSA1ICsgMwpgYGAKCipIb3dldmVyKiwgeW91IHNob3VsZCBzdGlsbCB1c2UgYD1gIHdoZW4geW91IGFyZSBzcGVjaWZ5aW5nCmFuIGFyZ3VtZW50IGluIGEgZnVuY3Rpb24gY2FsbCwgZS5nLiBpbiB0aGUgZm9sbG93aW5nIHNheSBgbj0xMGAsIG5vdCBgbjwtMTBgCgpgYGB7cn0KaGVhZCh3b3Jkcywgbj0zKQpgYGAKCkdlbmVyYWxseSBzcGVha2luZywgc3BhY2VzIGFuZCBicmFja2V0cyBhcmUgb3B0aW9uYWwgaW4gUiwgYnV0IG1ha2UgY29kZSBtb3JlIHJlYWRhYmxlCgpgYGB7cn0KNSszLzJeMi00ID09IDUgKyAoMyAvIDJeMikgLSA0CgppZGVudGljYWwoeDwtNSszLCB4IDwtIDUgKyAzKQoKaWRlbnRpY2FsKG1heCgxLCAyLCAzKSwgbWF4KDEsMiwzKSkKYGBgCgpCdXQgYmUgY2FyZWZ1bCAtLSB0aGUgYXNzaWdubWVudCBvcGVyYXRvciBgPC1gIGlzIGEgdW5pdCArIGNhbid0IGJlIHNwbGl0IHVwIQpUaGUgZmlyc3QgbGluZSB3aWxsIGJlIGludGVycHJldGVkIGFzIHRoZSAoZmFsc2UpIHN0YXRlbWVudCB0aGF0IHRoZSB2YWx1ZSBvZiB0aGUgdmFyaWFibGUgJ3gnIGlzIGxlc3MgdGhhbiB0aGUgc3VtIG9mIC01IGFuIDMgKGkuZS4gLTIpLCBidXQgdGhlIHNlY29uZCBvbmUgd29uJ3QuLi4KCmBgYHtyfQp4IDwtIC01KzMgICAjIHN0b3JlcyAtMiBpbiB2YXIgeAp4PCAtNSszICAgICAjIGdpdmVzIEZBTFNFIQpgYGAKCgpZb3UgY2FuIHN0YXJ0IG5ldyBsaW5lcyBpbiB0aGUgbWlkZGxlIG9mIGNvbW1hbmRzIHRvbywgd2hpY2ggc29tZXRpbWVzIG1ha2VzCmZvciBjbGVhbmVyICsgZWFzaWVyIHRvIHJlYWQgY29kZSAobm90ZSB0aGUgYXV0by1pbmRlbnRpbmcgb2YgUlN0dWRpbykKCgpgYGB7cn0KdGhlV29yZHMgPC0KICBjKCJ0aGVGaXJzdFdvcmQiLCJ0aGVTZWNvbmRXb3JkIiwidGhlVGhpcmRXb3JkIiwidGhlRm91cnRoV29yZCIsCiAgICAidGhlRmlmdGhXb3JkIiwidGhlU2l4dGhXb3JkIiwidGhlU2V2ZW50aFdvcmQiKQpgYGAKCgpCdXQgYWdhaW4sIGJlIGNhcmVmdWwhIElmIHdlIHRyeSB0aGUgZm9sbG93aW5nLCB3ZSB3aWxsIGdldCBhbiBlcnJvci4KCmBgYHtyfQojIyB0aGVXb3JkcwojIyAgIDwtICAgYygidGhlRmlyc3RXb3JkIiwidGhlU2Vjb25kV29yZCIsInRoZVRoaXJkV29yZCIsInRoZUZvdXJ0aFdvcmQiLAojIyAgICAgICAgInRoZUZpZnRoV29yZCIsInRoZVNpeHRoV29yZCIsInRoZVNldmVudGhXb3JkIikKIyMgCiMjIEVycm9yOiB1bmV4cGVjdGVkICcsJyBpbiAiICAgICAgICJ0aGVGaWZ0aFdvcmQiLCIKYGBgCgoKVGhpcyBuZXh0IGNvbW1hbmQgd29uJ3QgdGhyb3cgYW4gZXJyb3IsIGJ1dCB3aWxsIGluc2VydCBhIG5ld2xpbmUgYW5kIHRhYiBjaGFyIGludG8gdGhlIHRoaXJkIGVsZW1lbnQsIHdoaWNoIHdlIHdhbnRlZCB0byBiZSBgdGhlVGhpcmRXb3JkYC4uLgoKYGBge3J9CnRoZVdvcmRzIDwtIGMoInRoZUZpcnN0V29yZCIsInRoZVNlY29uZFdvcmQiLCJ0aGVUaGlyZAogICAgICAgICAgICAgIFdvcmQiLCJ0aGVGb3VydGhXb3JkIiwKICAgICAgICAgICAgICAidGhlRmlmdGhXb3JkIiwidGhlU2l4dGhXb3JkIiwidGhlU2V2ZW50aFdvcmQiKQpgYGAKCgpCdXQgaW5zdGVhZCBnZXRzICBpbnRlcnByZXRlZCBhczoKCmBgYHtyfQp0aGVXb3Jkc1szXQpgYGAKCkl0IGlzIG9mdGVuIHVzZWZ1bCB0byBvcmdhbml6ZSBjb2RlIGludG8gKipibG9ja3MqKiwgd2hpY2ggeW91IGNhbiB0aGluayBvZgphcyAicGFyYWdyYXBocyIgLS0gZ3JvdXBzIG9mIHNlbnRlbmNlcy9jb21tYW5kcyB0aGF0IG1lYW4vZG8gYSBzaW5nbGUgdGhpbmcuIEFsc28gZ29vZCBwcmFjdGljZSB0byBwdXQgY29tbWVudHMgYmVmb3JlIGV2ZXJ5IGJsb2NrLCBvciBldmVuIG11bHRpcGxlIHRpbWVzIHdpdGhpbiBhIHNpbmdsZSBibG9jay4KCmBgYHtyfQojIHRoaXMgYmxvY2sgYnVpbGRzIGEgZGYgb2Ygd29yZHMgd2l0aCB0aGVpciBmcmVxJ3MgYW5kIGZyZXEgcmFua3MKd29yZExpc3QgPC0gYygidzEiLCJ3MiIsInczIiwidzQiLCJ3NSIpCndvcmRGcmVxIDwtIGMoMTAsNCwzLDEyLDEyKQp3b3JkUmFuayA8LSByYW5rKC13b3JkRnJlcSwgdGllcy5tZXRob2Q9Im1pbiIpIAp3b3JkSW5mbyA8LSBkYXRhLmZyYW1lKAogIHdvcmQ9d29yZExpc3QsCiAgZnJlcT13b3JkRnJlcSwKICByYW5rPXdvcmRSYW5rCikKCndvcmRJbmZvCmBgYAoKCkFhYWFhbmQgbm93IGl0J3MgdGltZSB0byBjbGVhbiB1cC4gVGhpcyBpcyBub3QgbmVjZXNzYXJ5LCBidXQgY2FuIGJlIGNvbWZvcnRpbmcgaWYgeW91IGFyZSBhIG5ldXJvdGljIHBlcnNvbiA6cCAKCmBgYHtyfQpybSh4KTsgcm0odGhlV29yZHMpOyBybSh3b3JkTGlzdCk7IHJtKHdvcmRGcmVxKTsgcm0od29yZFJhbmspOyBybSh3b3JkSW5mbykKYGBgCgpUaGF0J3MgYWxsIGZvciBub3csIGZvbGtzISBTaG9vdCBtZSBhbiBlbWFpbCBpZiB5b3Ugd2FudCB0byB0YWxrL2xlYXJuL2hlYXIgbW9yZSEKCjwhLS0gb25lIG1vcmUgdGhpbmc6IGl0J3MgZ29vZCBwcmFjdGljZSB0byBlbmQgYSBzY3JpcHQgd2l0aCBhIGJsYW5rIGxpbmUgLS0+CgoKCgoKPCEtLSBFTkQgT0YgRE9DVU1FTlQgSVMgSEVSRSAtLT4KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgpib2R5eyAvKiBOb3JtYWwgICovCiAgIGZvbnQtc2l6ZTogMTZweDsKfQp0ZCB7ICAvKiBUYWJsZSAgKi8KICAgZm9udC1zaXplOiA4cHg7Cn0KaDEgeyAvKiBIZWFkZXIgMSAqLwogZm9udC1zaXplOiAyNHB4OwogZm9udC13ZWlnaHQ6IGJvbGQ7CiBjb2xvcjogRGFya0dyZWVuOwp9CgpoMiB7IC8qIEhlYWRlciAyICovCiBmb250LXNpemU6IDIwcHg7CiBjb2xvcjogZ3JlZW47Cn0KaDMgeyAvKiBIZWFkZXIgMyAqLwogZm9udC1zaXplOiAxNnB4OwogY29sb3I6IGdyZWVuOwp9CjwhLS0gY29kZS5yeyAvKiBDb2RlIGJsb2NrICovIC0tPgo8IS0tICAgZm9udC1zaXplOiAxMXB4OyAtLT4KPCEtLSB9IC0tPgo8IS0tIHByZSB7IC8qIENvZGUgYmxvY2sgKi8gLS0+CjwhLS0gICBmb250LXNpemU6IDExcHggLS0+CjwhLS0gfSAtLT4KPC9zdHlsZT4KCmBgYHtyfQojIHdhbnQgdG8gZ2V0IGlubGluZSBzeW50YXggaGlnaGxpZ2h0aW5nCmxpYnJhcnkoaGlnaHIpCmBgYAoK