Factors allow us to represent a type of data that exclusive and categorical. These data may, or may not, be ordered and in most cases, we can think of these kinds of data to represent things like, treatment levels, sampling locations, etc.

I’m going to start with some days of the week because they are exclusive (e.g., you cannot be in both Monday and Wednesday at the same time). Factors are initially created from string objects, though you could use numeric data but that would be stupid because you should use things that are descriptive and strings are much better than that.

weekdays <- c("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday", "Sunday")
class( weekdays )
[1] "character"

I’m going to take these days and random sample them to create a vector of 40 elements. This is something we do all the time and there is a sample() function that allows us to draw random samples either with or without replacement (e.g., can you select the same value more than once).

data <- sample( weekdays, size=40, replace=TRUE)
data
 [1] "Saturday"  "Saturday"  "Thursday"  "Monday"    "Thursday"  "Tuesday"   "Monday"    "Thursday"  "Monday"    "Saturday"  "Wednesday"
[12] "Thursday"  "Tuesday"   "Friday"    "Friday"    "Wednesday" "Saturday"  "Sunday"    "Friday"    "Wednesday" "Saturday"  "Wednesday"
[23] "Monday"    "Monday"    "Wednesday" "Monday"    "Tuesday"   "Tuesday"   "Saturday"  "Wednesday" "Wednesday" "Tuesday"   "Saturday" 
[34] "Sunday"    "Thursday"  "Sunday"    "Wednesday" "Saturday"  "Saturday"  "Sunday"   

These data are still

class( data )
[1] "character"

To turn them into a factor, we use…. factor()

days <- factor( data )
is.factor( days )
[1] TRUE
class( days )
[1] "factor"

Now when we look at the data, it looks a lot like it did before except for the last line which shows you the unique levels for elements in the vector.

days
 [1] Saturday  Saturday  Thursday  Monday    Thursday  Tuesday   Monday    Thursday  Monday    Saturday  Wednesday Thursday  Tuesday  
[14] Friday    Friday    Wednesday Saturday  Sunday    Friday    Wednesday Saturday  Wednesday Monday    Monday    Wednesday Monday   
[27] Tuesday   Tuesday   Saturday  Wednesday Wednesday Tuesday   Saturday  Sunday    Thursday  Sunday    Wednesday Saturday  Saturday 
[40] Sunday   
Levels: Friday Monday Saturday Sunday Thursday Tuesday Wednesday
summary( days )
   Friday    Monday  Saturday    Sunday  Thursday   Tuesday Wednesday 
        3         6         9         4         5         5         8 

We can put them into data frames and they know how to summarize themselves properly by counting the number of occurances of each level.

df <- data.frame( ID = 1:40, Weekdays = days )
summary( df )
       ID             Weekdays
 Min.   : 1.00   Friday   :3  
 1st Qu.:10.75   Monday   :6  
 Median :20.50   Saturday :9  
 Mean   :20.50   Sunday   :4  
 3rd Qu.:30.25   Thursday :5  
 Max.   :40.00   Tuesday  :5  
                 Wednesday:8  

And we can directly access the unique levels

levels( days )
[1] "Friday"    "Monday"    "Saturday"  "Sunday"    "Thursday"  "Tuesday"   "Wednesday"

So factors can be categorical (e.g., one is just different than the next) and compared via == and != values. Or they can be ordinal such that > and < make sense.

By default, a factor is not ordered.

is.ordered( days )
[1] FALSE
days[1] < days[2]
‘<’ not meaningful for factors
[1] NA
data <- factor( days, ordered=TRUE )
data 
 [1] Saturday  Saturday  Thursday  Monday    Thursday  Tuesday   Monday    Thursday  Monday    Saturday  Wednesday Thursday  Tuesday  
[14] Friday    Friday    Wednesday Saturday  Sunday    Friday    Wednesday Saturday  Wednesday Monday    Monday    Wednesday Monday   
[27] Tuesday   Tuesday   Saturday  Wednesday Wednesday Tuesday   Saturday  Sunday    Thursday  Sunday    Wednesday Saturday  Saturday 
[40] Sunday   
Levels: Friday < Monday < Saturday < Sunday < Thursday < Tuesday < Wednesday

So that if we go and try to order them, the only way they can be sorted is alphabetically.

sort( data )
 [1] Friday    Friday    Friday    Monday    Monday    Monday    Monday    Monday    Monday    Saturday  Saturday  Saturday  Saturday 
[14] Saturday  Saturday  Saturday  Saturday  Saturday  Sunday    Sunday    Sunday    Sunday    Thursday  Thursday  Thursday  Thursday 
[27] Thursday  Tuesday   Tuesday   Tuesday   Tuesday   Tuesday   Wednesday Wednesday Wednesday Wednesday Wednesday Wednesday Wednesday
[40] Wednesday
Levels: Friday < Monday < Saturday < Sunday < Thursday < Tuesday < Wednesday

However, this does not make sense. Who in their right mind would like to have Friday followed immediately by Monday? That is just not right!

To establish an ordinal variable with a specified sequence of values that are not alphabetical we need to pass along the levels themselves.

data <- factor( days, ordered=TRUE, levels = weekdays )
data
 [1] Saturday  Saturday  Thursday  Monday    Thursday  Tuesday   Monday    Thursday  Monday    Saturday  Wednesday Thursday  Tuesday  
[14] Friday    Friday    Wednesday Saturday  Sunday    Friday    Wednesday Saturday  Wednesday Monday    Monday    Wednesday Monday   
[27] Tuesday   Tuesday   Saturday  Wednesday Wednesday Tuesday   Saturday  Sunday    Thursday  Sunday    Wednesday Saturday  Saturday 
[40] Sunday   
Levels: Monday < Tuesday < Wednesday < Thursday < Friday < Saturday < Sunday

Now they’ll sort properly.

sort( data )
 [1] Monday    Monday    Monday    Monday    Monday    Monday    Tuesday   Tuesday   Tuesday   Tuesday   Tuesday   Wednesday Wednesday
[14] Wednesday Wednesday Wednesday Wednesday Wednesday Wednesday Thursday  Thursday  Thursday  Thursday  Thursday  Friday    Friday   
[27] Friday    Saturday  Saturday  Saturday  Saturday  Saturday  Saturday  Saturday  Saturday  Saturday  Sunday    Sunday    Sunday   
[40] Sunday   
Levels: Monday < Tuesday < Wednesday < Thursday < Friday < Saturday < Sunday

Exclusivity of Factor Levels

Once you establish a factor, you cannot set the values to anyting that is outside of the pre-defined levels. If you do, it will just put in missing data NA.

days[3] <- "Bob"
invalid factor level, NA generated
days
 [1] Saturday  Saturday  <NA>      Monday    Thursday  Tuesday   Monday    Thursday  Monday    Saturday  Wednesday Thursday  Tuesday  
[14] Friday    Friday    Wednesday Saturday  Sunday    Friday    Wednesday Saturday  Wednesday Monday    Monday    Wednesday Monday   
[27] Tuesday   Tuesday   Saturday  Wednesday Wednesday Tuesday   Saturday  Sunday    Thursday  Sunday    Wednesday Saturday  Saturday 
[40] Sunday   
Levels: Friday Monday Saturday Sunday Thursday Tuesday Wednesday

That being said, we can have more levels in the factor than observed in the data. Here is an example of just grabbing the work days from the week but making the levels equal to all the potential weekdays.

workdays <- sample( weekdays[1:5], size=40, replace = TRUE )
workdays <- factor( workdays, ordered=TRUE, levels = weekdays )

And when we summarize it, we see that while it is possible that days may be named Saturday and Sunday, they are not recoreded in the data we have for workdays.

summary( workdays )
   Monday   Tuesday Wednesday  Thursday    Friday  Saturday    Sunday 
       11         6         9         3        11         0         0 

We can drop the levels that have no representation

workdays <- droplevels( workdays ) 
summary( workdays )
   Monday   Tuesday Wednesday  Thursday    Friday 
       11         6         9         3        11 

forcats

The forcats library has a bunch of helper functions for working with factors. This is a relatively small library in tidyverse but a powerful one. I would recommend looking at the cheatsheet for it to get a more broad understanding of what functions in this library can do.

library( forcats )

Just like stringr had the str_ prefix, all the functions here have the fct_ prefix. Here are some examples.

Counting how many of each factor

fct_count( data )

Lumping Rare Factors

lumped <- fct_lump_min( data, min = 5 )
fct_count( lumped )

Reordering Factor Levels by Frequency

freq <- fct_infreq( data )
levels( freq )
[1] "Saturday"  "Wednesday" "Monday"    "Tuesday"   "Thursday"  "Sunday"    "Friday"   

Reordering by Order of Appearance

ordered <- fct_inorder( data )
levels( ordered )
[1] "Saturday"  "Thursday"  "Monday"    "Tuesday"   "Wednesday" "Friday"    "Sunday"   

Reordering Specific Levels

newWeek <- fct_relevel( data, "Sunday")
levels( newWeek )
[1] "Sunday"    "Monday"    "Tuesday"   "Wednesday" "Thursday"  "Friday"    "Saturday" 

Dropping Unobserved Levels - just like droplevels()

dropped <- fct_drop( workdays )
summary( dropped )
   Monday   Tuesday Wednesday  Thursday    Friday 
       11         6         9         3        11 

Using Factors

It is common to use factors as an organizing princple in our data. For example, let’s say we went out and sampled three different species of plants and measured characteristics of their flower size. The iris data set from R.A. Fisher is a classid data set that is include in R and it looks like this.

iris

By default it is a data.frame object.

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

By the by

One helpful function in base R is the by() function. It has the following form.

by( data, index, function)

The data is the raw data you are using, the index is a vector that we are using to differentiate among the species (the factor), and the function is what function we want to use.

So for example, if I were interesed in the mean length of the Sepal for each species, I could write.

meanSepalLength <- by( iris$Sepal.Length, iris$Species, mean )
class( meanSepalLength )
[1] "by"
meanSepalLength
iris$Species: setosa
[1] 5.006
----------------------------------------------------------------------------------------------------------- 
iris$Species: versicolor
[1] 5.936
----------------------------------------------------------------------------------------------------------- 
iris$Species: virginica
[1] 6.588

I could also do the same thing with the variance in sepal length.

by( iris[,2], iris[,5], var ) -> varSepalLength
varSepalLength 
iris[, 5]: setosa
[1] 0.1436898
----------------------------------------------------------------------------------------------------------- 
iris[, 5]: versicolor
[1] 0.09846939
----------------------------------------------------------------------------------------------------------- 
iris[, 5]: virginica
[1] 0.1040041

Using these kinds of functions we can create a summary data frame.


df <- tibble( Species = levels( iris$Species), 
              Average = meanSepalLength,
              Variance = varSepalLength
)
df

Missing Data

Missing data is a .red[fact of life] and R is very opinionated about how it handles missing values. In general, missing data is encoded as NA and is a valid entry for any data type (character, numeric, logical, factor, etc.). Where this becomes tricky is when we are doing operations on data that has missing values. R could take two routes:

  1. It could ignore the data and give you the answer directly as if the data were not missing, or
  2. It could let you know that there is missing data and make you do something about it.

Fortunately, R took the second route.

An example from the iris data, I’m going to add some missing data to it.


missingIris <- iris[, 4:5]
missingIris$Petal.Width[ c(2,6,12) ] <- NA
summary( missingIris )
  Petal.Width          Species  
 Min.   :0.100   setosa    :50  
 1st Qu.:0.300   versicolor:50  
 Median :1.300   virginica :50  
 Mean   :1.218                  
 3rd Qu.:1.800                  
 Max.   :2.500                  
 NA's   :3                      

Notice how the missing data is denoted in the summary.

Indications of Missing Data

When we perform a mathematical or statistical operation on data that has missing elements R will always return NA as the result.

mean( missingIris$Petal.Width )
[1] NA

This warns you that .red[at least one] of the observations in the data is missing.

Same output for using by(), it will put NA into each level that has at least one missing value.

by( missingIris$Petal.Width, missingIris$Species, mean )
missingIris$Species: setosa
[1] NA
----------------------------------------------------------------------------------------------------------- 
missingIris$Species: versicolor
[1] 1.326
----------------------------------------------------------------------------------------------------------- 
missingIris$Species: virginica
[1] 2.026

Working with Missing Data

To acknowledge that there are missing data and you still want the values, you need to tell the function you are using that data is missing and you are OK with that using the optional argument na.rm=TRUE (na = missing data & rm is remove).

mean( missingIris$Petal.Width, na.rm=TRUE)
[1] 1.218367

To pass this to the by() function, we add the optional argument na.rm=TRUE and by() passes it along to the mean function as “…”

by( missingIris$Petal.Width, missingIris$Species, mean, na.rm=TRUE )
missingIris$Species: setosa
[1] 0.2446809
----------------------------------------------------------------------------------------------------------- 
missingIris$Species: versicolor
[1] 1.326
----------------------------------------------------------------------------------------------------------- 
missingIris$Species: virginica
[1] 2.026

Fancy Tables

Making data frames like that above is a classic maneuver in R and I’m going to use this to introduce the use of the knitr library to show you how to take a set of data and turn it into a table for your manuscript.

library( knitr )

Now we can make a table as:

kable( df )
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408

We can even add a caption to it.

irisTable <- kable( df, caption = "The mean and variance in measured sepal length (in cm) for three species of Iris.")
irisTable
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408

In addition to this basic library, there is an kableExtra one that allows us to get even more fancy. You must go check out this webpage (which is an RMarkdown page by the way) to see all the other ways you can fancy up your tables.

library( kableExtra )

Table Themes

Here are some examples Themes

kable_paper( irisTable )
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408
kable_classic( irisTable )
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408
kable_classic_2( irisTable )
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408
kable_minimal( irisTable )
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408
kable_material( irisTable,lightable_options = c("striped", "hover") )
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408
kable_material_dark( irisTable )
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408

Table Sizes and Positions

We can be specific about the size and location of the whole table.

kable_paper(irisTable, full_width = FALSE )
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408
kable_paper( irisTable, full_width=FALSE, position="right")
The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408

And even embed it in a bunch of text and float it to left or right (I added echo=FALSE to the chunck header so it hides itself).

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut blandit libero sit amet porta elementum. In imperdiet tellus non odio porttitor auctor ac sit amet diam. Suspendisse eleifend vel nisi nec efficitur. Ut varius urna lectus, ac iaculis velit bibendum eget. Curabitur dignissim magna eu odio sagittis blandit. Vivamus sed ipsum mi. Etiam est leo, mollis ultrices dolor eget, consectetur euismod augue. In hac habitasse platea dictumst. Integer blandit ante magna, quis volutpat velit varius hendrerit. Vestibulum sit amet lacinia magna. Sed at varius nisl. Donec eu porta tellus, vitae rhoncus velit.

The mean and variance in measured sepal length (in cm) for three species of Iris.
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408

Maecenas euismod mattis neque. Ut at sapien lacinia, vehicula felis vitae, laoreet odio. Cras ut magna sed sapien scelerisque auctor maximus tincidunt arcu. Praesent vel accumsan leo. Etiam tempor leo placerat, commodo ante eu, posuere ligula. Sed purus justo, feugiat vel volutpat in, faucibus quis sem. Vivamus enim lacus, ultrices id erat in, posuere fringilla est. Nulla porttitor ac nunc nec efficitur. Duis tincidunt metus leo, at lacinia orci tristique in.

Nulla nec elementum nibh, quis congue augue. Vivamus fermentum nec mauris nec vehicula. Proin laoreet sapien quis orci mollis, et condimentum ante tempor. Vivamus hendrerit ut sem a iaculis. Quisque mauris enim, accumsan sit amet fermentum quis, convallis a nisl. Donec elit orci, consectetur id vestibulum in, elementum nec magna. In lobortis erat velit. Nam sit amet finibus arcu.

Heading Judo

We can do some really cool stuff on row and column headings. Here is an example where I add another row above the data columns for output.

classic <- kable_paper( irisTable )
add_header_above( classic, c(" " = 1, "Sepal Length (cm)" = 2))
The mean and variance in measured sepal length (in cm) for three species of Iris.
Sepal Length (cm)
Species Average Variance
setosa 5.006 0.14368980
versicolor 5.936 0.09846939
virginica 6.588 0.10400408
LS0tCnRpdGxlOiAiRmFjdG9yIERhdGEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHN0YXJ0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKbGlicmFyeSggdGlkeXZlcnNlICkKYGBgCgoKCkZhY3RvcnMgYWxsb3cgdXMgdG8gcmVwcmVzZW50IGEgdHlwZSBvZiBkYXRhIHRoYXQgZXhjbHVzaXZlIGFuZCBjYXRlZ29yaWNhbC4gIFRoZXNlIGRhdGEgbWF5LCBvciBtYXkgbm90LCBiZSBvcmRlcmVkIGFuZCBpbiBtb3N0IGNhc2VzLCB3ZSBjYW4gdGhpbmsgb2YgdGhlc2Uga2luZHMgb2YgZGF0YSB0byByZXByZXNlbnQgdGhpbmdzIGxpa2UsIHRyZWF0bWVudCBsZXZlbHMsIHNhbXBsaW5nIGxvY2F0aW9ucywgZXRjLgoKCgpJJ20gZ29pbmcgdG8gc3RhcnQgd2l0aCBzb21lIGRheXMgb2YgdGhlIHdlZWsgYmVjYXVzZSB0aGV5IGFyZSAqZXhjbHVzaXZlKiAoZS5nLiwgeW91IGNhbm5vdCBiZSBpbiBib3RoIE1vbmRheSBhbmQgV2VkbmVzZGF5IGF0IHRoZSBzYW1lIHRpbWUpLiAgRmFjdG9ycyBhcmUgaW5pdGlhbGx5IGNyZWF0ZWQgZnJvbSBzdHJpbmcgb2JqZWN0cywgdGhvdWdoIHlvdSBjb3VsZCB1c2UgYG51bWVyaWNgIGRhdGEgYnV0IHRoYXQgd291bGQgYmUgKnN0dXBpZCogYmVjYXVzZSB5b3Ugc2hvdWxkIHVzZSB0aGluZ3MgdGhhdCBhcmUgZGVzY3JpcHRpdmUgYW5kIHN0cmluZ3MgYXJlIG11Y2ggYmV0dGVyIHRoYW4gdGhhdC4KCmBgYHtyfQp3ZWVrZGF5cyA8LSBjKCJNb25kYXkiLCJUdWVzZGF5IiwiV2VkbmVzZGF5IiwiVGh1cnNkYXkiLCJGcmlkYXkiLCJTYXR1cmRheSIsICJTdW5kYXkiKQpjbGFzcyggd2Vla2RheXMgKQpgYGAKCkknbSBnb2luZyB0byB0YWtlIHRoZXNlIGRheXMgYW5kIHJhbmRvbSBzYW1wbGUgdGhlbSB0byBjcmVhdGUgYSB2ZWN0b3Igb2YgNDAgZWxlbWVudHMuICBUaGlzIGlzIHNvbWV0aGluZyB3ZSBkbyBhbGwgdGhlIHRpbWUgYW5kIHRoZXJlIGlzIGEgYHNhbXBsZSgpYCBmdW5jdGlvbiB0aGF0IGFsbG93cyB1cyB0byBkcmF3IHJhbmRvbSBzYW1wbGVzIGVpdGhlciB3aXRoIG9yIHdpdGhvdXQgcmVwbGFjZW1lbnQgKGUuZy4sIGNhbiB5b3Ugc2VsZWN0IHRoZSBzYW1lIHZhbHVlIG1vcmUgdGhhbiBvbmNlKS4KCmBgYHtyfQpkYXRhIDwtIHNhbXBsZSggd2Vla2RheXMsIHNpemU9NDAsIHJlcGxhY2U9VFJVRSkKZGF0YQpgYGAKVGhlc2UgZGF0YSBhcmUgc3RpbGwgCgpgYGB7cn0KY2xhc3MoIGRhdGEgKQpgYGAKClRvIHR1cm4gdGhlbSBpbnRvIGEgZmFjdG9yLCB3ZSB1c2UuLi4uIGBmYWN0b3IoKWAKCmBgYHtyfQpkYXlzIDwtIGZhY3RvciggZGF0YSApCmlzLmZhY3RvciggZGF5cyApCmNsYXNzKCBkYXlzICkKYGBgCgoKTm93IHdoZW4gd2UgbG9vayBhdCB0aGUgZGF0YSwgaXQgbG9va3MgYSBsb3QgbGlrZSBpdCBkaWQgYmVmb3JlICpleGNlcHQgZm9yIHRoZSBsYXN0IGxpbmUqIHdoaWNoIHNob3dzIHlvdSB0aGUgdW5pcXVlIGxldmVscyBmb3IgZWxlbWVudHMgaW4gdGhlIHZlY3Rvci4KCmBgYHtyfQpkYXlzCnN1bW1hcnkoIGRheXMgKQpgYGAKCldlIGNhbiBwdXQgdGhlbSBpbnRvIGRhdGEgZnJhbWVzIGFuZCB0aGV5IGtub3cgaG93IHRvIHN1bW1hcml6ZSB0aGVtc2VsdmVzIHByb3Blcmx5IGJ5IGNvdW50aW5nIHRoZSBudW1iZXIgb2Ygb2NjdXJhbmNlcyBvZiBlYWNoIGxldmVsLgoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoIElEID0gMTo0MCwgV2Vla2RheXMgPSBkYXlzICkKc3VtbWFyeSggZGYgKQpgYGAKCkFuZCB3ZSBjYW4gZGlyZWN0bHkgYWNjZXNzIHRoZSB1bmlxdWUgbGV2ZWxzIAoKYGBge3J9CmxldmVscyggZGF5cyApCmBgYAoKU28gZmFjdG9ycyBjYW4gYmUgY2F0ZWdvcmljYWwgKGUuZy4sIG9uZSBpcyBqdXN0IGRpZmZlcmVudCB0aGFuIHRoZSBuZXh0KSBhbmQgY29tcGFyZWQgdmlhIGA9PWAgYW5kIGAhPWAgdmFsdWVzLiAgT3IgdGhleSBjYW4gYmUgb3JkaW5hbCBzdWNoIHRoYXQgYD5gIGFuZCBgPGAgbWFrZSBzZW5zZS4KCkJ5IGRlZmF1bHQsIGEgYGZhY3RvcmAgaXMgbm90IG9yZGVyZWQuCgpgYGB7cn0KaXMub3JkZXJlZCggZGF5cyApCmRheXNbMV0gPCBkYXlzWzJdCmBgYAoKYGBge3J9CmRhdGEgPC0gZmFjdG9yKCBkYXlzLCBvcmRlcmVkPVRSVUUgKQpkYXRhIApgYGAKClNvIHRoYXQgaWYgd2UgZ28gYW5kIHRyeSB0byBvcmRlciB0aGVtLCB0aGUgb25seSB3YXkgdGhleSBjYW4gYmUgc29ydGVkIGlzIGFscGhhYmV0aWNhbGx5LgoKYGBge3J9CnNvcnQoIGRhdGEgKQpgYGAKCkhvd2V2ZXIsIHRoaXMgZG9lcyBub3QgbWFrZSBzZW5zZS4gIFdobyBpbiB0aGVpciByaWdodCBtaW5kIHdvdWxkIGxpa2UgdG8gaGF2ZSBgRnJpZGF5YCBmb2xsb3dlZCBpbW1lZGlhdGVseSBieSBgTW9uZGF5YD8gIFRoYXQgaXMganVzdCBub3QgcmlnaHQhCgpUbyBlc3RhYmxpc2ggYW4gb3JkaW5hbCB2YXJpYWJsZSB3aXRoIGEgc3BlY2lmaWVkIHNlcXVlbmNlIG9mIHZhbHVlcyB0aGF0IGFyZSAqbm90IGFscGhhYmV0aWNhbCogd2UgbmVlZCB0byBwYXNzIGFsb25nIHRoZSBsZXZlbHMgdGhlbXNlbHZlcy4KCgpgYGB7cn0KZGF0YSA8LSBmYWN0b3IoIGRheXMsIG9yZGVyZWQ9VFJVRSwgbGV2ZWxzID0gd2Vla2RheXMgKQpkYXRhCmBgYAoKTm93IHRoZXknbGwgc29ydCBwcm9wZXJseS4KCmBgYHtyfQpzb3J0KCBkYXRhICkKYGBgCiMjIEV4Y2x1c2l2aXR5IG9mIEZhY3RvciBMZXZlbHMKCk9uY2UgeW91IGVzdGFibGlzaCBhIGZhY3RvciwgeW91IGNhbm5vdCBzZXQgdGhlIHZhbHVlcyB0byBhbnl0aW5nIHRoYXQgaXMgb3V0c2lkZSBvZiB0aGUgcHJlLWRlZmluZWQgbGV2ZWxzLiAgSWYgeW91IGRvLCBpdCB3aWxsIGp1c3QgcHV0IGluIG1pc3NpbmcgZGF0YSBgTkFgLgoKYGBge3J9CmRheXNbM10gPC0gIkJvYiIKZGF5cwpgYGAKClRoYXQgYmVpbmcgc2FpZCwgd2UgY2FuIGhhdmUgbW9yZSBsZXZlbHMgaW4gdGhlIGZhY3RvciB0aGFuIG9ic2VydmVkIGluIHRoZSBkYXRhLiAgSGVyZSBpcyBhbiBleGFtcGxlIG9mIGp1c3QgZ3JhYmJpbmcgdGhlIHdvcmsgZGF5cyBmcm9tIHRoZSB3ZWVrIGJ1dCBtYWtpbmcgdGhlIGxldmVscyBlcXVhbCB0byBhbGwgdGhlIHBvdGVudGlhbCB3ZWVrZGF5cy4KCmBgYHtyfQp3b3JrZGF5cyA8LSBzYW1wbGUoIHdlZWtkYXlzWzE6NV0sIHNpemU9NDAsIHJlcGxhY2UgPSBUUlVFICkKd29ya2RheXMgPC0gZmFjdG9yKCB3b3JrZGF5cywgb3JkZXJlZD1UUlVFLCBsZXZlbHMgPSB3ZWVrZGF5cyApCmBgYAoKQW5kIHdoZW4gd2Ugc3VtbWFyaXplIGl0LCB3ZSBzZWUgdGhhdCB3aGlsZSBpdCBpcyBwb3NzaWJsZSB0aGF0IGRheXMgbWF5IGJlIG5hbWVkIGBTYXR1cmRheWAgYW5kIGBTdW5kYXlgLCB0aGV5IGFyZSBub3QgcmVjb3JlZGVkIGluIHRoZSBkYXRhIHdlIGhhdmUgZm9yIHdvcmtkYXlzLgoKYGBge3J9CnN1bW1hcnkoIHdvcmtkYXlzICkKYGBgCgpXZSBjYW4gZHJvcCB0aGUgbGV2ZWxzIHRoYXQgaGF2ZSBubyByZXByZXNlbnRhdGlvbgoKCgpgYGB7cn0Kd29ya2RheXMgPC0gZHJvcGxldmVscyggd29ya2RheXMgKSAKc3VtbWFyeSggd29ya2RheXMgKQoKYGBgCgojIyBgZm9yY2F0c2AKClRoZSBgZm9yY2F0c2AgbGlicmFyeSBoYXMgYSBidW5jaCBvZiBoZWxwZXIgZnVuY3Rpb25zIGZvciB3b3JraW5nIHdpdGggZmFjdG9ycy4gIFRoaXMgaXMgYSByZWxhdGl2ZWx5IHNtYWxsIGxpYnJhcnkgaW4gYHRpZHl2ZXJzZWAgYnV0IGEgcG93ZXJmdWwgb25lLiAgSSB3b3VsZCByZWNvbW1lbmQgbG9va2luZyBhdCB0aGUgW2NoZWF0c2hlZXRdKCkgZm9yIGl0IHRvIGdldCBhIG1vcmUgYnJvYWQgdW5kZXJzdGFuZGluZyBvZiB3aGF0IGZ1bmN0aW9ucyBpbiB0aGlzIGxpYnJhcnkgY2FuIGRvLgoKYGBge3J9CmxpYnJhcnkoIGZvcmNhdHMgKQpgYGAKCi0tCgpKdXN0IGxpa2UgYHN0cmluZ3JgIGhhZCB0aGUgYHN0cl9gIHByZWZpeCwgYWxsIHRoZSBmdW5jdGlvbnMgaGVyZSBoYXZlIHRoZSBgZmN0X2AgcHJlZml4LiAgSGVyZSBhcmUgc29tZSBleGFtcGxlcy4KCipDb3VudGluZyBob3cgbWFueSBvZiBlYWNoIGZhY3RvcioKCmBgYHtyfQpmY3RfY291bnQoIGRhdGEgKQpgYGAKCgoqTHVtcGluZyBSYXJlIEZhY3RvcnMqCgpgYGB7cn0KbHVtcGVkIDwtIGZjdF9sdW1wX21pbiggZGF0YSwgbWluID0gNSApCmZjdF9jb3VudCggbHVtcGVkICkKYGBgCgoqUmVvcmRlcmluZyBGYWN0b3IgTGV2ZWxzIGJ5IEZyZXF1ZW5jeSogCgpgYGB7cn0KZnJlcSA8LSBmY3RfaW5mcmVxKCBkYXRhICkKbGV2ZWxzKCBmcmVxICkKYGBgCgoKKlJlb3JkZXJpbmcgYnkgT3JkZXIgb2YgQXBwZWFyYW5jZSoKCmBgYHtyfQpvcmRlcmVkIDwtIGZjdF9pbm9yZGVyKCBkYXRhICkKbGV2ZWxzKCBvcmRlcmVkICkKYGBgCgoKKlJlb3JkZXJpbmcgU3BlY2lmaWMgTGV2ZWxzKgoKYGBge3J9Cm5ld1dlZWsgPC0gZmN0X3JlbGV2ZWwoIGRhdGEsICJTdW5kYXkiKQpsZXZlbHMoIG5ld1dlZWsgKQpgYGAKCipEcm9wcGluZyBVbm9ic2VydmVkIExldmVscyogLSBqdXN0IGxpa2UgYGRyb3BsZXZlbHMoKWAKCmBgYHtyfQpkcm9wcGVkIDwtIGZjdF9kcm9wKCB3b3JrZGF5cyApCnN1bW1hcnkoIGRyb3BwZWQgKQpgYGAKCgoKCiMjIFVzaW5nIEZhY3RvcnMgCgpJdCBpcyBjb21tb24gdG8gdXNlIGZhY3RvcnMgYXMgYW4gb3JnYW5pemluZyBwcmluY3BsZSBpbiBvdXIgZGF0YS4gIEZvciBleGFtcGxlLCBsZXQncyBzYXkgd2Ugd2VudCBvdXQgYW5kIHNhbXBsZWQgdGhyZWUgZGlmZmVyZW50IHNwZWNpZXMgb2YgcGxhbnRzIGFuZCBtZWFzdXJlZCBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlaXIgZmxvd2VyIHNpemUuICBUaGUgYGlyaXNgIGRhdGEgc2V0IGZyb20gUi5BLiBGaXNoZXIgaXMgYSBjbGFzc2lkIGRhdGEgc2V0IHRoYXQgaXMgaW5jbHVkZSBpbiBgUmAgYW5kIGl0IGxvb2tzIGxpa2UgdGhpcy4KCgpgYGB7cn0KaXJpcwpgYGAKCkJ5IGRlZmF1bHQgaXQgaXMgYSBgZGF0YS5mcmFtZWAgb2JqZWN0LgoKYGBge3J9CmNsYXNzKCBpcmlzICkKYGBgCgojIyMgQnkgIHRoZSBieQoKT25lIGhlbHBmdWwgZnVuY3Rpb24gaW4gYmFzZSBgUmAgaXMgdGhlIGBieSgpYCBmdW5jdGlvbi4gIEl0IGhhcyB0aGUgZm9sbG93aW5nIGZvcm0uCgpgYGAKYnkoIGRhdGEsIGluZGV4LCBmdW5jdGlvbikKYGBgCgpUaGUgYGRhdGFgIGlzIHRoZSByYXcgZGF0YSB5b3UgYXJlIHVzaW5nLCB0aGUgYGluZGV4YCBpcyBhIHZlY3RvciB0aGF0IHdlIGFyZSB1c2luZyB0byBkaWZmZXJlbnRpYXRlIGFtb25nIHRoZSBzcGVjaWVzICh0aGUgZmFjdG9yKSwgYW5kIHRoZSBmdW5jdGlvbiBpcyB3aGF0IGZ1bmN0aW9uIHdlIHdhbnQgdG8gdXNlLgoKU28gZm9yIGV4YW1wbGUsIGlmIEkgd2VyZSBpbnRlcmVzZWQgaW4gdGhlIG1lYW4gbGVuZ3RoIG9mIHRoZSBTZXBhbCBmb3IgZWFjaCBzcGVjaWVzLCBJIGNvdWxkIHdyaXRlLgoKYGBge3J9Cm1lYW5TZXBhbExlbmd0aCA8LSBieSggaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU3BlY2llcywgbWVhbiApCmNsYXNzKCBtZWFuU2VwYWxMZW5ndGggKQptZWFuU2VwYWxMZW5ndGgKYGBgCkkgY291bGQgYWxzbyBkbyB0aGUgc2FtZSB0aGluZyB3aXRoIHRoZSB2YXJpYW5jZSBpbiBzZXBhbCBsZW5ndGguCgpgYGB7cn0KYnkoIGlyaXNbLDJdLCBpcmlzWyw1XSwgdmFyICkgLT4gdmFyU2VwYWxMZW5ndGgKdmFyU2VwYWxMZW5ndGggCmBgYAoKClVzaW5nIHRoZXNlIGtpbmRzIG9mIGZ1bmN0aW9ucyB3ZSBjYW4gY3JlYXRlIGEgc3VtbWFyeSBkYXRhIGZyYW1lLgoKYGBge3J9CgpkZiA8LSB0aWJibGUoIFNwZWNpZXMgPSBsZXZlbHMoIGlyaXMkU3BlY2llcyksIAogICAgICAgICAgICAgIEF2ZXJhZ2UgPSBtZWFuU2VwYWxMZW5ndGgsCiAgICAgICAgICAgICAgVmFyaWFuY2UgPSB2YXJTZXBhbExlbmd0aAopCmRmCmBgYAoKCiMjIE1pc3NpbmcgRGF0YSAKCgpNaXNzaW5nIGRhdGEgaXMgYSAucmVkW2ZhY3Qgb2YgbGlmZV0gYW5kIGBSYCBpcyB2ZXJ5IG9waW5pb25hdGVkIGFib3V0IGhvdyBpdCBoYW5kbGVzIG1pc3NpbmcgdmFsdWVzLiAgSW4gZ2VuZXJhbCwgbWlzc2luZyBkYXRhIGlzIGVuY29kZWQgYXMgYE5BYCBhbmQgaXMgYSB2YWxpZCBlbnRyeSBmb3IgYW55IGRhdGEgdHlwZSAoY2hhcmFjdGVyLCBudW1lcmljLCBsb2dpY2FsLCBmYWN0b3IsIGV0Yy4pLiAgV2hlcmUgdGhpcyBiZWNvbWVzIHRyaWNreSBpcyB3aGVuIHdlIGFyZSBkb2luZyBvcGVyYXRpb25zIG9uIGRhdGEgdGhhdCBoYXMgbWlzc2luZyB2YWx1ZXMuICBgUmAgY291bGQgdGFrZSB0d28gcm91dGVzOiAgCgoxLiBJdCBjb3VsZCBpZ25vcmUgdGhlIGRhdGEgYW5kIGdpdmUgeW91IHRoZSBhbnN3ZXIgZGlyZWN0bHkgYXMgaWYgdGhlIGRhdGEgd2VyZSBub3QgbWlzc2luZywgb3IgIAoyLiBJdCBjb3VsZCBsZXQgeW91IGtub3cgdGhhdCB0aGVyZSBpcyBtaXNzaW5nIGRhdGEgYW5kIG1ha2UgeW91IGRvIHNvbWV0aGluZyBhYm91dCBpdC4KCkZvcnR1bmF0ZWx5LCBgUmAgdG9vayB0aGUgc2Vjb25kIHJvdXRlLgoKQW4gZXhhbXBsZSBmcm9tIHRoZSBpcmlzIGRhdGEsIEknbSBnb2luZyB0byBhZGQgc29tZSBtaXNzaW5nIGRhdGEgdG8gaXQuCgpgYGB7cn0KCm1pc3NpbmdJcmlzIDwtIGlyaXNbLCA0OjVdCm1pc3NpbmdJcmlzJFBldGFsLldpZHRoWyBjKDIsNiwxMikgXSA8LSBOQQpzdW1tYXJ5KCBtaXNzaW5nSXJpcyApCmBgYAoKTm90aWNlIGhvdyB0aGUgbWlzc2luZyBkYXRhIGlzIGRlbm90ZWQgaW4gdGhlIHN1bW1hcnkuCgoKIyMjIEluZGljYXRpb25zIG9mIE1pc3NpbmcgRGF0YQoKV2hlbiB3ZSBwZXJmb3JtIGEgbWF0aGVtYXRpY2FsIG9yIHN0YXRpc3RpY2FsIG9wZXJhdGlvbiBvbiBkYXRhIHRoYXQgaGFzIG1pc3NpbmcgZWxlbWVudHMgYFJgIHdpbGwgKiphbHdheXMqKiByZXR1cm4gTkEgYXMgdGhlIHJlc3VsdC4KCmBgYHtyfQptZWFuKCBtaXNzaW5nSXJpcyRQZXRhbC5XaWR0aCApCmBgYAoKVGhpcyB3YXJucyB5b3UgdGhhdCAucmVkW2F0IGxlYXN0IG9uZV0gb2YgdGhlIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YSBpcyBtaXNzaW5nLgoKU2FtZSBvdXRwdXQgZm9yIHVzaW5nIGBieSgpYCwgaXQgd2lsbCBwdXQgYE5BYCBpbnRvIGVhY2ggbGV2ZWwgdGhhdCBoYXMgYXQgbGVhc3Qgb25lIG1pc3NpbmcgdmFsdWUuCgpgYGB7cn0KYnkoIG1pc3NpbmdJcmlzJFBldGFsLldpZHRoLCBtaXNzaW5nSXJpcyRTcGVjaWVzLCBtZWFuICkKYGBgCgoKIyMjIFdvcmtpbmcgd2l0aCBNaXNzaW5nIERhdGEKClRvIGFja25vd2xlZGdlIHRoYXQgdGhlcmUgYXJlIG1pc3NpbmcgZGF0YSBhbmQgeW91IHN0aWxsIHdhbnQgdGhlIHZhbHVlcywgeW91IG5lZWQgdG8gdGVsbCB0aGUgZnVuY3Rpb24geW91IGFyZSB1c2luZyB0aGF0IGRhdGEgaXMgbWlzc2luZyBhbmQgeW91IGFyZSBPSyB3aXRoIHRoYXQgdXNpbmcgdGhlIG9wdGlvbmFsIGFyZ3VtZW50IGBuYS5ybT1UUlVFYCAoYG5hYCA9IG1pc3NpbmcgZGF0YSAmIGBybWAgaXMgKnJlbW92ZSopLgoKYGBge3J9Cm1lYW4oIG1pc3NpbmdJcmlzJFBldGFsLldpZHRoLCBuYS5ybT1UUlVFKQpgYGAKClRvIHBhc3MgdGhpcyB0byB0aGUgYGJ5KClgIGZ1bmN0aW9uLCB3ZSBhZGQgdGhlIG9wdGlvbmFsIGFyZ3VtZW50IGBuYS5ybT1UUlVFYCBhbmQgYGJ5KClgIHBhc3NlcyBpdCBhbG9uZyB0byB0aGUgYG1lYW5gIGZ1bmN0aW9uIGFzICIuLi4iCgpgYGB7cn0KYnkoIG1pc3NpbmdJcmlzJFBldGFsLldpZHRoLCBtaXNzaW5nSXJpcyRTcGVjaWVzLCBtZWFuLCBuYS5ybT1UUlVFICkKYGBgCgoKCgojIyBGYW5jeSBUYWJsZXMKCk1ha2luZyBkYXRhIGZyYW1lcyBsaWtlIHRoYXQgYWJvdmUgaXMgYSAqY2xhc3NpYyogbWFuZXV2ZXIgaW4gYFJgIGFuZCBJJ20gZ29pbmcgdG8gdXNlIHRoaXMgdG8gaW50cm9kdWNlIHRoZSB1c2Ugb2YgdGhlIGBrbml0cmAgbGlicmFyeSB0byBzaG93IHlvdSBob3cgdG8gdGFrZSBhIHNldCBvZiBkYXRhIGFuZCB0dXJuIGl0IGludG8gYSB0YWJsZSBmb3IgeW91ciBtYW51c2NyaXB0LgoKYGBge3J9CmxpYnJhcnkoIGtuaXRyICkKYGBgCgpOb3cgd2UgY2FuIG1ha2UgYSB0YWJsZSBhczoKCmBgYHtyfQprYWJsZSggZGYgKQpgYGAKCldlIGNhbiBldmVuIGFkZCBhIGNhcHRpb24gdG8gaXQuCgpgYGB7cn0KaXJpc1RhYmxlIDwtIGthYmxlKCBkZiwgY2FwdGlvbiA9ICJUaGUgbWVhbiBhbmQgdmFyaWFuY2UgaW4gbWVhc3VyZWQgc2VwYWwgbGVuZ3RoIChpbiBjbSkgZm9yIHRocmVlIHNwZWNpZXMgb2YgSXJpcy4iKQppcmlzVGFibGUKYGBgCgoKSW4gYWRkaXRpb24gdG8gdGhpcyBiYXNpYyBsaWJyYXJ5LCB0aGVyZSBpcyBhbiBga2FibGVFeHRyYWAgb25lIHRoYXQgYWxsb3dzIHVzIHRvIGdldCBldmVuIG1vcmUgZmFuY3kuICBZb3UgbXVzdCBnbyBjaGVjayBvdXQgW3RoaXNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9rYWJsZUV4dHJhL3ZpZ25ldHRlcy9hd2Vzb21lX3RhYmxlX2luX2h0bWwuaHRtbCkgd2VicGFnZSAod2hpY2ggaXMgYW4gUk1hcmtkb3duIHBhZ2UgYnkgdGhlIHdheSkgdG8gc2VlIGFsbCB0aGUgb3RoZXIgd2F5cyB5b3UgY2FuIGZhbmN5IHVwIHlvdXIgdGFibGVzLgoKCmBgYHtyfQpsaWJyYXJ5KCBrYWJsZUV4dHJhICkKYGBgCgoKIyMjIFRhYmxlIFRoZW1lcyAKCkhlcmUgYXJlIHNvbWUgZXhhbXBsZXMgVGhlbWVzCgpgYGB7cn0Ka2FibGVfcGFwZXIoIGlyaXNUYWJsZSApCmBgYAoKYGBge3J9CmthYmxlX2NsYXNzaWMoIGlyaXNUYWJsZSApCmBgYAoKYGBge3J9CmthYmxlX2NsYXNzaWNfMiggaXJpc1RhYmxlICkKYGBgCgoKYGBge3J9CmthYmxlX21pbmltYWwoIGlyaXNUYWJsZSApCmBgYAoKYGBge3J9CmthYmxlX21hdGVyaWFsKCBpcmlzVGFibGUsbGlnaHRhYmxlX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikgKQpgYGAKCmBgYHtyfQprYWJsZV9tYXRlcmlhbF9kYXJrKCBpcmlzVGFibGUgKQpgYGAKCgojIyBUYWJsZSBTaXplcyBhbmQgUG9zaXRpb25zCgpXZSBjYW4gYmUgc3BlY2lmaWMgYWJvdXQgdGhlIHNpemUgYW5kIGxvY2F0aW9uIG9mIHRoZSB3aG9sZSB0YWJsZS4KCgpgYGB7cn0Ka2FibGVfcGFwZXIoaXJpc1RhYmxlLCBmdWxsX3dpZHRoID0gRkFMU0UgKQpgYGAKCmBgYHtyfQprYWJsZV9wYXBlciggaXJpc1RhYmxlLCBmdWxsX3dpZHRoPUZBTFNFLCBwb3NpdGlvbj0icmlnaHQiKQpgYGAKCkFuZCBldmVuIGVtYmVkIGl0IGluIGEgYnVuY2ggb2YgdGV4dCBhbmQgZmxvYXQgaXQgdG8gbGVmdCBvciByaWdodCAoSSBhZGRlZCBgZWNobz1GQUxTRWAgdG8gdGhlIGNodW5jayBoZWFkZXIgc28gaXQgaGlkZXMgaXRzZWxmKS4gIAoKTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gVXQgYmxhbmRpdCBsaWJlcm8gc2l0IGFtZXQgcG9ydGEgZWxlbWVudHVtLiBJbiBpbXBlcmRpZXQgdGVsbHVzIG5vbiBvZGlvIHBvcnR0aXRvciBhdWN0b3IgYWMgc2l0IGFtZXQgZGlhbS4gU3VzcGVuZGlzc2UgZWxlaWZlbmQgdmVsIG5pc2kgbmVjIGVmZmljaXR1ci4gVXQgdmFyaXVzIHVybmEgbGVjdHVzLCBhYyBpYWN1bGlzIHZlbGl0IGJpYmVuZHVtIGVnZXQuIEN1cmFiaXR1ciBkaWduaXNzaW0gbWFnbmEgZXUgb2RpbyBzYWdpdHRpcyBibGFuZGl0LiBWaXZhbXVzIHNlZCBpcHN1bSBtaS4gRXRpYW0gZXN0IGxlbywgbW9sbGlzIHVsdHJpY2VzIGRvbG9yIGVnZXQsIGNvbnNlY3RldHVyIGV1aXNtb2QgYXVndWUuIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBJbnRlZ2VyIGJsYW5kaXQgYW50ZSBtYWduYSwgcXVpcyB2b2x1dHBhdCB2ZWxpdCB2YXJpdXMgaGVuZHJlcml0LiBWZXN0aWJ1bHVtIHNpdCBhbWV0IGxhY2luaWEgbWFnbmEuIFNlZCBhdCB2YXJpdXMgbmlzbC4gRG9uZWMgZXUgcG9ydGEgdGVsbHVzLCB2aXRhZSByaG9uY3VzIHZlbGl0LgoKYGBge3IgZWNobz1GQUxTRX0Ka2FibGVfcGFwZXIoIGlyaXNUYWJsZSwgZnVsbF93aWR0aD1GQUxTRSwgcG9zaXRpb24gPSAiZmxvYXRfcmlnaHQiKQpgYGAKCk1hZWNlbmFzIGV1aXNtb2QgbWF0dGlzIG5lcXVlLiBVdCBhdCBzYXBpZW4gbGFjaW5pYSwgdmVoaWN1bGEgZmVsaXMgdml0YWUsIGxhb3JlZXQgb2Rpby4gQ3JhcyB1dCBtYWduYSBzZWQgc2FwaWVuIHNjZWxlcmlzcXVlIGF1Y3RvciBtYXhpbXVzIHRpbmNpZHVudCBhcmN1LiBQcmFlc2VudCB2ZWwgYWNjdW1zYW4gbGVvLiBFdGlhbSB0ZW1wb3IgbGVvIHBsYWNlcmF0LCBjb21tb2RvIGFudGUgZXUsIHBvc3VlcmUgbGlndWxhLiBTZWQgcHVydXMganVzdG8sIGZldWdpYXQgdmVsIHZvbHV0cGF0IGluLCBmYXVjaWJ1cyBxdWlzIHNlbS4gVml2YW11cyBlbmltIGxhY3VzLCB1bHRyaWNlcyBpZCBlcmF0IGluLCBwb3N1ZXJlIGZyaW5naWxsYSBlc3QuIE51bGxhIHBvcnR0aXRvciBhYyBudW5jIG5lYyBlZmZpY2l0dXIuIER1aXMgdGluY2lkdW50IG1ldHVzIGxlbywgYXQgbGFjaW5pYSBvcmNpIHRyaXN0aXF1ZSBpbi4KCk51bGxhIG5lYyBlbGVtZW50dW0gbmliaCwgcXVpcyBjb25ndWUgYXVndWUuIFZpdmFtdXMgZmVybWVudHVtIG5lYyBtYXVyaXMgbmVjIHZlaGljdWxhLiBQcm9pbiBsYW9yZWV0IHNhcGllbiBxdWlzIG9yY2kgbW9sbGlzLCBldCBjb25kaW1lbnR1bSBhbnRlIHRlbXBvci4gVml2YW11cyBoZW5kcmVyaXQgdXQgc2VtIGEgaWFjdWxpcy4gUXVpc3F1ZSBtYXVyaXMgZW5pbSwgYWNjdW1zYW4gc2l0IGFtZXQgZmVybWVudHVtIHF1aXMsIGNvbnZhbGxpcyBhIG5pc2wuIERvbmVjIGVsaXQgb3JjaSwgY29uc2VjdGV0dXIgaWQgdmVzdGlidWx1bSBpbiwgZWxlbWVudHVtIG5lYyBtYWduYS4gSW4gbG9ib3J0aXMgZXJhdCB2ZWxpdC4gTmFtIHNpdCBhbWV0IGZpbmlidXMgYXJjdS4KCiMjIyBIZWFkaW5nIEp1ZG8KCldlIGNhbiBkbyBzb21lIHJlYWxseSBjb29sIHN0dWZmIG9uIHJvdyBhbmQgY29sdW1uIGhlYWRpbmdzLiAgSGVyZSBpcyBhbiBleGFtcGxlIHdoZXJlIEkgYWRkIGFub3RoZXIgcm93IGFib3ZlIHRoZSBkYXRhIGNvbHVtbnMgZm9yIG91dHB1dC4KCmBgYHtyfQpjbGFzc2ljIDwtIGthYmxlX3BhcGVyKCBpcmlzVGFibGUgKQphZGRfaGVhZGVyX2Fib3ZlKCBjbGFzc2ljLCBjKCIgIiA9IDEsICJTZXBhbCBMZW5ndGggKGNtKSIgPSAyKSkKYGBgCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo=