Getting Started

Please install software in this order before we begin:

  1. R from https://cran.r-project.org/
  2. RStudio from https://www.rstudio.com/products/rstudio/#Desktop

Then open RStudio and run the code below in the Console pane of RStudio.

install.packages("tidyverse") # Copy on the Console command line and hit Return or Enter

Introduction to R

R is a free and open-source programming language for statistical computing and graphics. It is supported by a multinational collaborative team known as the ‘R core team’. These people update R on a continuous basis. The current R version should be 3.4.2 - Short Summer (note that version names are all selected from old Peanuts comics). However, in using R you will use packages that have been written by others. You can learn more about R from https://www.r-project.org/.

The software can be downloaded from https://cran.r-project.org/, The Comprehensive R Archive Network. This archive stores packages that you can download and use for specific purposes. For example, if you work with geographic GIS data you may want to download the rgeos package for calculating distances taking into account Earth’s curvature. Or maybe you work with financial data and you need to do bond valuations so you could use the quantmod package. However, to use either package or on of their alternatives, we need to become familiar with basic R commands.

Introduction to RStudio

RStudio is not R. RStudio is a private company that produces the RStudio integrated development environment for R. Nevertheless, in keeping with the general ethos of R, the RStudio IDE is also free and open-source. RStudio is essentially a working platform that will let you get more out of R. It can be downloaded from https://www.rstudio.com/products/rstudio/#Desktop. Keep in mind, however, that when you are running RStudio you are running two programs. This is important because updating one does not update the other. RStudio makes R much more user friendly so we will be using in this workshop.

R Demo

To me, “programming” is the art of getting other people to do your work for you. People new to using computers this way are often under the false assumption that you need to be building elaborate projects from the ground up. This might be somewhat true for languages like C++ (I don’t know. I don’t use these langauges.). But, for interpreted languages like Python, and R, once you’ve become familiar with the language, the trick is to know what tools have already been built for you.

For example, imagine if someone asked you to create a map of Edmonton that showed all of the Edmonton Public Library branches that would display both the branch name and the most reserved book at that location when clicked. A task like that might seem impossible. However, that’s what the 6 lines of code below do.

EPL <- read.csv(url("https://data.edmonton.ca/api/views/qdgm-hex6/rows.csv?accessType=DOWNLOAD"), na.strings = "")
EPL_loc <- read.csv(url("https://data.edmonton.ca/api/views/jn25-zspi/rows.csv?accessType=DOWNLOAD"), na.strings = "") # These lines read in two .csv files from the YEG Open Data Portal.

names(EPL)[names(EPL) == "Branch.ID"] <-  "BRANCH_ID"
EPL <- left_join(EPL, EPL_loc, by = "BRANCH_ID") # We need to join the two 'dataframes' and the important column had a slightly different name in each.

# We don't need all the data, just the data for the book that had the maximum number of holds at each branch.
EPL_clip <- EPL %>%
    group_by(BRANCH_ID) %>% 
    arrange(Number.of.Holds) %>% 
    filter(Number.of.Holds == max(Number.of.Holds))

# And, this line puts it all together.
leaflet() %>% 
    setView(lng = -113.504655, lat = 53.546629, zoom = 11) %>%
    addProviderTiles(providers$Hydda.Full) %>%
    addCircles(EPL_clip$LONGITUDE, lat= EPL_clip$LATITUDE, popup=paste(EPL_clip$Title, EPL_clip$Branch.Name)) %>%
    addMarkers(lng= -113.526340, lat= 53.522938, popup="University of Alberta")

# I've even added a few things here. This also does what we want. I just doesn't look quite as nice.
# leaflet() %>% 
#   addProviderTiles(providers$Hydda.Full) %>%
#   addCircles(EPL_clip$LONGITUDE, lat= EPL_clip$LATITUDE, popup=paste(EPL_clip$Title, EPL_clip$Branch.Name))

R as a Calculator

Basics

At its heart, R is just an elaborate calculator.

2 + 2 # Adds the two numbers
4 - 2
5 * 4
 100 / 5
101 /5
101 %/% 5 # Just gives the number of complete times 5 goes into 100.
101 %% 5 # Modulo just gives the remainder.

Assigning Result Values to an Object

Note that only the final line prints an output. The others create an object in your Global Environment.

a <- 2 + 2 # Assigns the result to the object 'a'
b = 3 + 3 # The equals sign also works for assignment, but I will always use " <- " to assign something to an object.
c <- a + b
c # Returns the value of c

Basic Data Classes

The objects we create can be of different types. One of the most basic types is a vector, an object with more than one value. Vectors, however, can only have a single data type, or class. Knowing the data class you are working with is extremely important. When you experience problems in R, besides typos, like leaving out a commma or bracket, trying to perform an operation on the wrong kind of data is one of the most common types of errors.

Some simple number vectors:

vec_a <- 1:20
vec_b <- c(1, 4, 8, 9, 3, 2) # We use c() to concatenate values
vec_c <- rep(c(vec_b), each = 3) # instead of 'each =' we could also use 'times =' or 'length.out ='
vec_d <- c(1.1, 3.3)

Some simple character vectors:

peopleNames <- c("Hadley", "Jenny", "Hilary", "Yihui")
vec_f <- c("1", "2", "3", "4", "5")

A logical vector

logical_a <- c(TRUE, FALSE, FALSE, TRUE, FALSE)

The str() function, short for ‘structure’, is extremely useful for determining the data class (or object type) of an object. See the difference between vec_b and vec_f. To us, they both include numbers, but to R vec_b is a numeric vector and vec_f is a character vector.

str(vec_b)
str(vec_f)

Try running str() on several of the other objects we’ve created.

Coercion

So what happens if we try to create vector out of different data classes? Let’s find out!

vec_g <- c(1, "one", TRUE)
str(vec_g)

What happened? Why?

Let’s try another example:

vec_h <- c(1, TRUE, 0, FALSE)
str(vec_h)

Did the same thing happen? What’s the explanation for this?

Factors

Factors are more or less just another data class, but vectors need to be told that they are factors for them to be so.

fac_a <- factor(c("red", "blue", "green"))
str(fac_a)

Factors can be unordered like fac_a, where each colour could just be an attribute of a real item being counted, like a car, or factors can be ordered. Ordered factors make sense for things that have levels like ‘High’, ‘Medium’, and ‘Low’; or ‘First’, ‘Second’ and ‘Third’, etc.

fac_b <- factor(c("Good", "Better", "Good", "Best", "Better"), levels = c("Good", "Better", "Best"),  ordered = TRUE)
str(fac_b)

So what happens if we try to add an element to the factor?

vec_i <- c(fac_b, TRUE)
str(vec_i)
vec_j <- c(fac_b, "Bad")
str(vec_j)

Be warned!

Basic Object Types

R objects can come in a variety of types. The most commonly used are vectors (including factors), matrices, data frames and lists. We have covered vectors. Matrices are similar to vectors in that they can only include a single data class, but are two dimensional, having both rows and columns.

A simple matrix:

mat_a <- matrix( c('a','a','b','c','b','a'), nrow = 2, ncol = 3, byrow = TRUE)
mat_a

Changing the byrow = option:

mat_b <- matrix( c('a','a','b','c','b','a'), nrow = 2, ncol = 3, byrow = FALSE)
mat_b

A numeric matrix:

mat_c <- matrix(1:6, nrow = 2, ncol = 3, byrow = TRUE)
mat_c

Multiplying by matrices:

mat_d <- matrix(1:6, nrow = 2, ncol = 3, byrow = FALSE)
mat_c * mat_d

If you work with data formatted in spreadsheets, and I think most people do, you will most often keep your data in a data frame. Data frames are great because they can take multiple data classes stored in different columns. R comes with some example datasets, most of which are not very interesting. You will often see tutorials on the web that use either mtcars or iris in their examples. These do not show up in your environment pane, but they are there. Let’s look at the iris data.

First let’s look at the dimensions of iris.

dim(iris)

It has 150 rows and 5 columns. We probably do not want to just output the 150 rows into our console so we can get a look at the data in a couple of ways. We can look at the head() of the data (or even tail()).

head(iris)

This gives us the first 6 rows as a default. head(iris, 8) would give us the first 8 rows. If we run str() on iris we see that there are indeed different data classes in one object. The Species column is a factor, while all the other columns are numeric.

str(iris)

You can create data frames yourself by using data.frame() on vectors that have equal length.

vec_a <- 1:12 # This deletes our old vec_a permanently and without warning.
vec_b <- sample(1:100, size = 12, replace = TRUE) # Randomly sampling 12 numbers between 1 and 100 with replacement (meaning you could get the number 88 twice). You will get different numbers every time you run this.
fac_a <- rep(fac_a, times = 4) # fac_a had a length() of 3 but now it is length 12
peopleNames <- rep(peopleNames, each = 3) # What happens if you run this line and the one above it multiple times (DON'T TRY IT.)

df_a <- data.frame(vec_a, vec_b, fac_a, peopleNames)
head(df_a)

There is a lot to know about data frames, but we’ll save that for the Reading Data into R and Cleaning Data sections.

The final object is a list. Lists can be comprised of anything. They can be very useful, but we will not talk much about them here. The following code creates a list of the data frames mtcars and iris, a single character value, a single numeric value, a logical vector, and I even included a character string for good measure.

list_a <- list(mtcars, iris, "a", 1, c(TRUE, FALSE, FALSE), "the kitchen sink")

We will talk more about how to get values out of lists, data frames and matrices later.

Working Directory and Creating an R Project

When working with R it is important to know your working directory. You can find out your working directory by running:

getwd()

You can set your working directory by inserting a pathname into setwd(), or by doing this in RStudio by clicking on the Session dropdown menu.

setwd("/Users/brian/Dropbox/db_documents/HomeRwork/DataCarpentry")

A good way to keep your R work organized, however, is to create an R Project. Projects are created in their own folder and will keep R scripts, and saved plots or data there. You can create a new project by clicking ong the R Project Icon in the top right corner of your RStudio window and selecting ‘New Project’. You can choose either to create a new directory (folder) or to use an existing one. Let’s all create a project in a folder called ‘DataCarpentry’.

A Few Tips and Tricks Going Forward

The RStudio IDE has many useful commands for making programming in R easier. You can learn about more of these by downloading the RStudion cheatsheet at https://www.rstudio.com/resources/cheatsheets/. (The others are great, too - especially the data vis and data transformation ones.)

Try this: Click on the console and press the up arrow on your keyboard. It gives the last command that you ran. Pretty standard stuff. However, if you want to know about the last few times you ran a particular command, or created certain objects type a few letters and then press Ctrl (Windows) or Cmd (Mac) and Up. Let’s see the code we used to create the vectors. Type vec, then Cmd + Up. This is extremely useful. (Unless otherwise noted, if Mac is Cmd, Windows is Ctrl.)

Some other useful shortcuts:

  1. Cmd+Shift+F - Look for text in a folder.
  2. Cmd+Shift+C - Comments or uncomments a block of code.
  3. Shift+Alt+Up/Down (Win); Cmd+Option+Up/Down (Mac) - Copies the present line of code above or below.
  4. Cmd+Option+Click (Try Shift+Alt+Click on Windows) - Creates multiple cursors.

A Word about R Scripts, Undo, and Reproducability

  1. Open a new R Script (Top left corner).
  2. Type: newVec <- 1:12.
  3. With your cursor at the end of that line, press Cmd+Return.
  4. Change the line you typed to: newVec <- "Oops!"
  5. Run step 3 again.
  6. Try to undo.

What happens? This is one of the best features of R (or similar ways of analyzing data like Python). This behaviour means we should alter the way many of us typically use software and think about how to make our work easily reproducible. While many new users think of the objects as they exist within the R Environment as the important output of their labour this view is incorrect. What you should be aiming to produce is a reproducable R script. This does not mean that most R Scripts will not contain a bunch of rough R code. It means that at least one of your open scripts should contain good work that contains lots of comments that would help you (or someone else) if you came back to it six months later. The command rm(list = ls()) removes everything in your R Environment (but not your History). If you are doing things correctly, you have a script that you could simply ‘source’ and get everything back again if everything were to be deleted. Working this way is the ideal. I do not always fully do this, and some datasets or models take a loooong time to load and run so just re-running a script can sometime be impractical, but the point here is that you should not get too attached to objects in your environment, but rather pay attention to the code that created them.

Up Next: Reading In and Cleaning Data

LS0tCnRpdGxlOiAnRGF0YSBDYXJwZW50cnkgMTogSW50cm8gdG8gUicKYXV0aG9yOiAiQnJpYW4gUnVzayIKZGF0ZTogIk5vdmVtYmVyIDE1dGggJiAxNnRoLCAyMDE3IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobGVhZmxldCkKYGBgCgojIyBHZXR0aW5nIFN0YXJ0ZWQKClBsZWFzZSBpbnN0YWxsIHNvZnR3YXJlIGluIHRoaXMgb3JkZXIgYmVmb3JlIHdlIGJlZ2luOgoKMS4gUiBmcm9tIDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy8+CjIuIFJTdHVkaW8gZnJvbSA8aHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby8jRGVza3RvcD4KClRoZW4gb3BlbiBSU3R1ZGlvIGFuZCBydW4gdGhlIGNvZGUgYmVsb3cgaW4gdGhlIGBDb25zb2xlYCBwYW5lIG9mIFJTdHVkaW8uCmBgYHtyLCBldmFsID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpICMgQ29weSBvbiB0aGUgQ29uc29sZSBjb21tYW5kIGxpbmUgYW5kIGhpdCBSZXR1cm4gb3IgRW50ZXIKYGBgCgojIyBJbnRyb2R1Y3Rpb24gdG8gUgoKUiBpcyBhIGZyZWUgYW5kIG9wZW4tc291cmNlIHByb2dyYW1taW5nIGxhbmd1YWdlIGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcgYW5kIGdyYXBoaWNzLiBJdCBpcyBzdXBwb3J0ZWQgYnkgYSBtdWx0aW5hdGlvbmFsIGNvbGxhYm9yYXRpdmUgdGVhbSBrbm93biBhcyB0aGUgJ1IgY29yZSB0ZWFtJy4gVGhlc2UgcGVvcGxlIHVwZGF0ZSBSIG9uIGEgY29udGludW91cyBiYXNpcy4gVGhlIGN1cnJlbnQgUiB2ZXJzaW9uIHNob3VsZCBiZSAzLjQuMiAtIGBTaG9ydCBTdW1tZXJgIChub3RlIHRoYXQgdmVyc2lvbiBuYW1lcyBhcmUgYWxsIHNlbGVjdGVkIGZyb20gb2xkIFBlYW51dHMgY29taWNzKS4gSG93ZXZlciwgaW4gdXNpbmcgUiB5b3Ugd2lsbCB1c2UgcGFja2FnZXMgdGhhdCBoYXZlIGJlZW4gd3JpdHRlbiBieSBvdGhlcnMuIFlvdSBjYW4gbGVhcm4gbW9yZSBhYm91dCBSIGZyb20gPGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvPi4KClRoZSBzb2Z0d2FyZSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIDxodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy8+LCBUaGUgQ29tcHJlaGVuc2l2ZSBSIEFyY2hpdmUgTmV0d29yay4gVGhpcyBhcmNoaXZlIHN0b3JlcyBwYWNrYWdlcyB0aGF0IHlvdSBjYW4gZG93bmxvYWQgYW5kIHVzZSBmb3Igc3BlY2lmaWMgcHVycG9zZXMuIEZvciBleGFtcGxlLCBpZiB5b3Ugd29yayB3aXRoIGdlb2dyYXBoaWMgR0lTIGRhdGEgeW91IG1heSB3YW50IHRvIGRvd25sb2FkIHRoZSBgcmdlb3NgIHBhY2thZ2UgZm9yIGNhbGN1bGF0aW5nIGRpc3RhbmNlcyB0YWtpbmcgaW50byBhY2NvdW50IEVhcnRoJ3MgY3VydmF0dXJlLiBPciBtYXliZSB5b3Ugd29yayB3aXRoIGZpbmFuY2lhbCBkYXRhIGFuZCB5b3UgbmVlZCB0byBkbyBib25kIHZhbHVhdGlvbnMgc28geW91IGNvdWxkIHVzZSB0aGUgYHF1YW50bW9kYCBwYWNrYWdlLiBIb3dldmVyLCB0byB1c2UgZWl0aGVyIHBhY2thZ2Ugb3Igb24gb2YgdGhlaXIgYWx0ZXJuYXRpdmVzLCB3ZSBuZWVkIHRvIGJlY29tZSBmYW1pbGlhciB3aXRoIGJhc2ljIFIgY29tbWFuZHMuCgojIyBJbnRyb2R1Y3Rpb24gdG8gUlN0dWRpbwoKUlN0dWRpbyBpcyBfX25vdF9fIFIuIFJTdHVkaW8gaXMgYSBwcml2YXRlIGNvbXBhbnkgdGhhdCBwcm9kdWNlcyB0aGUgUlN0dWRpbyBfaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudF8gZm9yIFIuIE5ldmVydGhlbGVzcywgaW4ga2VlcGluZyB3aXRoIHRoZSBnZW5lcmFsIGV0aG9zIG9mIFIsIHRoZSBSU3R1ZGlvIElERSBpcyBhbHNvIGZyZWUgYW5kIG9wZW4tc291cmNlLiBSU3R1ZGlvIGlzIGVzc2VudGlhbGx5IGEgd29ya2luZyBwbGF0Zm9ybSB0aGF0IHdpbGwgbGV0IHlvdSBnZXQgbW9yZSBvdXQgb2YgUi4gSXQgY2FuIGJlIGRvd25sb2FkZWQgZnJvbSA8aHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby8jRGVza3RvcD4uIEtlZXAgaW4gbWluZCwgaG93ZXZlciwgdGhhdCB3aGVuIHlvdSBhcmUgcnVubmluZyBSU3R1ZGlvIHlvdSBhcmUgcnVubmluZyB0d28gcHJvZ3JhbXMuIFRoaXMgaXMgaW1wb3J0YW50IGJlY2F1c2UgdXBkYXRpbmcgb25lIGRvZXMgbm90IHVwZGF0ZSB0aGUgb3RoZXIuIFJTdHVkaW8gbWFrZXMgUiBtdWNoIG1vcmUgdXNlciBmcmllbmRseSBzbyB3ZSB3aWxsIGJlIHVzaW5nIGluIHRoaXMgd29ya3Nob3AuCgojIyBSIERlbW8KClRvIG1lLCAicHJvZ3JhbW1pbmciIGlzIHRoZSBhcnQgb2YgZ2V0dGluZyBvdGhlciBwZW9wbGUgdG8gZG8geW91ciB3b3JrIGZvciB5b3UuIFBlb3BsZSBuZXcgdG8gdXNpbmcgY29tcHV0ZXJzIHRoaXMgd2F5IGFyZSBvZnRlbiB1bmRlciB0aGUgZmFsc2UgYXNzdW1wdGlvbiB0aGF0IHlvdSBuZWVkIHRvIGJlIGJ1aWxkaW5nIGVsYWJvcmF0ZSBwcm9qZWN0cyBmcm9tIHRoZSBncm91bmQgdXAuIFRoaXMgbWlnaHQgYmUgc29tZXdoYXQgdHJ1ZSBmb3IgbGFuZ3VhZ2VzIGxpa2UgQysrIChJIGRvbid0IGtub3cuIEkgZG9uJ3QgdXNlIHRoZXNlIGxhbmdhdWdlcy4pLiBCdXQsIGZvciBpbnRlcnByZXRlZCBsYW5ndWFnZXMgbGlrZSBQeXRob24sIGFuZCBSLCBvbmNlIHlvdSd2ZSBiZWNvbWUgZmFtaWxpYXIgd2l0aCB0aGUgbGFuZ3VhZ2UsIHRoZSB0cmljayBpcyB0byBrbm93IHdoYXQgdG9vbHMgaGF2ZSBhbHJlYWR5IGJlZW4gYnVpbHQgZm9yIHlvdS4gCgpGb3IgZXhhbXBsZSwgaW1hZ2luZSBpZiBzb21lb25lIGFza2VkIHlvdSB0byBjcmVhdGUgYSBtYXAgb2YgRWRtb250b24gdGhhdCBzaG93ZWQgYWxsIG9mIHRoZSBFZG1vbnRvbiBQdWJsaWMgTGlicmFyeSBicmFuY2hlcyB0aGF0IHdvdWxkIGRpc3BsYXkgYm90aCB0aGUgYnJhbmNoIG5hbWUgYW5kIHRoZSBtb3N0IHJlc2VydmVkIGJvb2sgYXQgdGhhdCBsb2NhdGlvbiB3aGVuIGNsaWNrZWQuIEEgdGFzayBsaWtlIHRoYXQgbWlnaHQgc2VlbSBpbXBvc3NpYmxlLiBIb3dldmVyLCB0aGF0J3Mgd2hhdCB0aGUgNiBsaW5lcyBvZiBjb2RlIGJlbG93IGRvLiAKCmBgYHtyfQpFUEwgPC0gcmVhZC5jc3YodXJsKCJodHRwczovL2RhdGEuZWRtb250b24uY2EvYXBpL3ZpZXdzL3FkZ20taGV4Ni9yb3dzLmNzdj9hY2Nlc3NUeXBlPURPV05MT0FEIiksIG5hLnN0cmluZ3MgPSAiIikKRVBMX2xvYyA8LSByZWFkLmNzdih1cmwoImh0dHBzOi8vZGF0YS5lZG1vbnRvbi5jYS9hcGkvdmlld3Mvam4yNS16c3BpL3Jvd3MuY3N2P2FjY2Vzc1R5cGU9RE9XTkxPQUQiKSwgbmEuc3RyaW5ncyA9ICIiKSAjIFRoZXNlIGxpbmVzIHJlYWQgaW4gdHdvIC5jc3YgZmlsZXMgZnJvbSB0aGUgWUVHIE9wZW4gRGF0YSBQb3J0YWwuCgpuYW1lcyhFUEwpW25hbWVzKEVQTCkgPT0gIkJyYW5jaC5JRCJdIDwtICAiQlJBTkNIX0lEIgpFUEwgPC0gbGVmdF9qb2luKEVQTCwgRVBMX2xvYywgYnkgPSAiQlJBTkNIX0lEIikgIyBXZSBuZWVkIHRvIGpvaW4gdGhlIHR3byAnZGF0YWZyYW1lcycgYW5kIHRoZSBpbXBvcnRhbnQgY29sdW1uIGhhZCBhIHNsaWdodGx5IGRpZmZlcmVudCBuYW1lIGluIGVhY2guCgojIFdlIGRvbid0IG5lZWQgYWxsIHRoZSBkYXRhLCBqdXN0IHRoZSBkYXRhIGZvciB0aGUgYm9vayB0aGF0IGhhZCB0aGUgbWF4aW11bSBudW1iZXIgb2YgaG9sZHMgYXQgZWFjaCBicmFuY2guCkVQTF9jbGlwIDwtIEVQTCAlPiUKCWdyb3VwX2J5KEJSQU5DSF9JRCkgJT4lIAoJYXJyYW5nZShOdW1iZXIub2YuSG9sZHMpICU+JSAKCWZpbHRlcihOdW1iZXIub2YuSG9sZHMgPT0gbWF4KE51bWJlci5vZi5Ib2xkcykpCgojIEFuZCwgdGhpcyBsaW5lIHB1dHMgaXQgYWxsIHRvZ2V0aGVyLgpsZWFmbGV0KCkgJT4lIAoJc2V0VmlldyhsbmcgPSAtMTEzLjUwNDY1NSwgbGF0ID0gNTMuNTQ2NjI5LCB6b29tID0gMTEpICU+JQoJYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkSHlkZGEuRnVsbCkgJT4lCglhZGRDaXJjbGVzKEVQTF9jbGlwJExPTkdJVFVERSwgbGF0PSBFUExfY2xpcCRMQVRJVFVERSwgcG9wdXA9cGFzdGUoRVBMX2NsaXAkVGl0bGUsIEVQTF9jbGlwJEJyYW5jaC5OYW1lKSkgJT4lCglhZGRNYXJrZXJzKGxuZz0gLTExMy41MjYzNDAsIGxhdD0gNTMuNTIyOTM4LCBwb3B1cD0iVW5pdmVyc2l0eSBvZiBBbGJlcnRhIikKCiMgSSd2ZSBldmVuIGFkZGVkIGEgZmV3IHRoaW5ncyBoZXJlLiBUaGlzIGFsc28gZG9lcyB3aGF0IHdlIHdhbnQuIEkganVzdCBkb2Vzbid0IGxvb2sgcXVpdGUgYXMgbmljZS4KIyBsZWFmbGV0KCkgJT4lIAojIAlhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRIeWRkYS5GdWxsKSAlPiUKIyAJYWRkQ2lyY2xlcyhFUExfY2xpcCRMT05HSVRVREUsIGxhdD0gRVBMX2NsaXAkTEFUSVRVREUsIHBvcHVwPXBhc3RlKEVQTF9jbGlwJFRpdGxlLCBFUExfY2xpcCRCcmFuY2guTmFtZSkpCmBgYAoKCiMjIFIgYXMgYSBDYWxjdWxhdG9yCgojIyMjIEJhc2ljcwpBdCBpdHMgaGVhcnQsIFIgaXMganVzdCBhbiBlbGFib3JhdGUgY2FsY3VsYXRvci4KCmBgYHtyfQoyICsgMiAjIEFkZHMgdGhlIHR3byBudW1iZXJzCmBgYAoKYGBge3J9CjQgLSAyCmBgYApgYGB7cn0KNSAqIDQKYGBgCgpgYGB7cn0KIDEwMCAvIDUKYGBgCmBgYHtyfQoxMDEgLzUKYGBgCgpgYGB7cn0KMTAxICUvJSA1ICMgSnVzdCBnaXZlcyB0aGUgbnVtYmVyIG9mIGNvbXBsZXRlIHRpbWVzIDUgZ29lcyBpbnRvIDEwMC4KYGBgCmBgYHtyfQoxMDEgJSUgNSAjIE1vZHVsbyBqdXN0IGdpdmVzIHRoZSByZW1haW5kZXIuCmBgYAoKIyMjIyBBc3NpZ25pbmcgUmVzdWx0IFZhbHVlcyB0byBhbiBPYmplY3QKCk5vdGUgdGhhdCBvbmx5IHRoZSBmaW5hbCBsaW5lIHByaW50cyBhbiBvdXRwdXQuIFRoZSBvdGhlcnMgY3JlYXRlIGFuIG9iamVjdCBpbiB5b3VyIGBHbG9iYWwgRW52aXJvbm1lbnRgLgpgYGB7cn0KYSA8LSAyICsgMiAjIEFzc2lnbnMgdGhlIHJlc3VsdCB0byB0aGUgb2JqZWN0ICdhJwpiID0gMyArIDMgIyBUaGUgZXF1YWxzIHNpZ24gYWxzbyB3b3JrcyBmb3IgYXNzaWdubWVudCwgYnV0IEkgd2lsbCBhbHdheXMgdXNlICIgPC0gIiB0byBhc3NpZ24gc29tZXRoaW5nIHRvIGFuIG9iamVjdC4KYyA8LSBhICsgYgpjICMgUmV0dXJucyB0aGUgdmFsdWUgb2YgYwpgYGAKCiMjIEJhc2ljIERhdGEgQ2xhc3NlcwoKVGhlIG9iamVjdHMgd2UgY3JlYXRlIGNhbiBiZSBvZiBkaWZmZXJlbnQgdHlwZXMuIE9uZSBvZiB0aGUgbW9zdCBiYXNpYyB0eXBlcyBpcyBhIGB2ZWN0b3JgLCBhbiBvYmplY3Qgd2l0aCBtb3JlIHRoYW4gb25lIHZhbHVlLiBWZWN0b3JzLCBob3dldmVyLCBjYW4gb25seSBoYXZlIGEgc2luZ2xlIGRhdGEgdHlwZSwgb3IgY2xhc3MuIEtub3dpbmcgdGhlIGRhdGEgY2xhc3MgeW91IGFyZSB3b3JraW5nIHdpdGggaXMgZXh0cmVtZWx5IGltcG9ydGFudC4gV2hlbiB5b3UgZXhwZXJpZW5jZSBwcm9ibGVtcyBpbiBSLCBiZXNpZGVzIHR5cG9zLCBsaWtlIGxlYXZpbmcgb3V0IGEgY29tbW1hIG9yIGJyYWNrZXQsIHRyeWluZyB0byBwZXJmb3JtIGFuIG9wZXJhdGlvbiBvbiB0aGUgd3Jvbmcga2luZCBvZiBkYXRhIGlzIG9uZSBvZiB0aGUgbW9zdCBjb21tb24gdHlwZXMgb2YgZXJyb3JzLgoKU29tZSBzaW1wbGUgbnVtYmVyIHZlY3RvcnM6CmBgYHtyfQp2ZWNfYSA8LSAxOjIwCmBgYAoKYGBge3J9CnZlY19iIDwtIGMoMSwgNCwgOCwgOSwgMywgMikgIyBXZSB1c2UgYygpIHRvIGNvbmNhdGVuYXRlIHZhbHVlcwpgYGAKCmBgYHtyfQp2ZWNfYyA8LSByZXAoYyh2ZWNfYiksIGVhY2ggPSAzKSAjIGluc3RlYWQgb2YgJ2VhY2ggPScgd2UgY291bGQgYWxzbyB1c2UgJ3RpbWVzID0nIG9yICdsZW5ndGgub3V0ID0nCmBgYAoKYGBge3J9CnZlY19kIDwtIGMoMS4xLCAzLjMpCmBgYAoKU29tZSBzaW1wbGUgY2hhcmFjdGVyIHZlY3RvcnM6CmBgYHtyfQpwZW9wbGVOYW1lcyA8LSBjKCJIYWRsZXkiLCAiSmVubnkiLCAiSGlsYXJ5IiwgIllpaHVpIikKYGBgCgpgYGB7cn0KdmVjX2YgPC0gYygiMSIsICIyIiwgIjMiLCAiNCIsICI1IikKYGBgCgpBIGxvZ2ljYWwgdmVjdG9yCmBgYHtyfQpsb2dpY2FsX2EgPC0gYyhUUlVFLCBGQUxTRSwgRkFMU0UsIFRSVUUsIEZBTFNFKQpgYGAKClRoZSBgc3RyKClgIGZ1bmN0aW9uLCBzaG9ydCBmb3IgJ3N0cnVjdHVyZScsIGlzIGV4dHJlbWVseSB1c2VmdWwgZm9yIGRldGVybWluaW5nIHRoZSBkYXRhIGNsYXNzIChvciBvYmplY3QgdHlwZSkgb2YgYW4gb2JqZWN0LiBTZWUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBgdmVjX2JgIGFuZCBgdmVjX2ZgLiBUbyB1cywgdGhleSBib3RoIGluY2x1ZGUgbnVtYmVycywgYnV0IHRvIFIgYHZlY19iYCBpcyBhIG51bWVyaWMgdmVjdG9yIGFuZCBgdmVjX2ZgIGlzIGEgY2hhcmFjdGVyIHZlY3Rvci4KCmBgYHtyfQpzdHIodmVjX2IpCmBgYApgYGB7cn0Kc3RyKHZlY19mKQpgYGAKClRyeSBydW5uaW5nIGBzdHIoKWAgb24gc2V2ZXJhbCBvZiB0aGUgb3RoZXIgb2JqZWN0cyB3ZSd2ZSBjcmVhdGVkLgoKIyMjIyBDb2VyY2lvbgoKU28gd2hhdCBoYXBwZW5zIGlmIHdlIHRyeSB0byBjcmVhdGUgdmVjdG9yIG91dCBvZiBkaWZmZXJlbnQgZGF0YSBjbGFzc2VzPyBMZXQncyBmaW5kIG91dCEKYGBge3J9CnZlY19nIDwtIGMoMSwgIm9uZSIsIFRSVUUpCnN0cih2ZWNfZykKYGBgCgpXaGF0IGhhcHBlbmVkPyBXaHk/CgpMZXQncyB0cnkgYW5vdGhlciBleGFtcGxlOgpgYGB7cn0KdmVjX2ggPC0gYygxLCBUUlVFLCAwLCBGQUxTRSkKc3RyKHZlY19oKQpgYGAKCkRpZCB0aGUgc2FtZSB0aGluZyBoYXBwZW4/IFdoYXQncyB0aGUgZXhwbGFuYXRpb24gZm9yIHRoaXM/CgojIyMjIEZhY3RvcnMKCkZhY3RvcnMgYXJlIG1vcmUgb3IgbGVzcyBqdXN0IGFub3RoZXIgZGF0YSBjbGFzcywgYnV0IHZlY3RvcnMgbmVlZCB0byBiZSB0b2xkIHRoYXQgdGhleSBhcmUgZmFjdG9ycyBmb3IgdGhlbSB0byBiZSBzby4KYGBge3J9CmZhY19hIDwtIGZhY3RvcihjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIpKQpzdHIoZmFjX2EpCmBgYAoKRmFjdG9ycyBjYW4gYmUgdW5vcmRlcmVkIGxpa2UgYGZhY19hYCwgd2hlcmUgZWFjaCBjb2xvdXIgY291bGQganVzdCBiZSBhbiBhdHRyaWJ1dGUgb2YgYSByZWFsIGl0ZW0gYmVpbmcgY291bnRlZCwgbGlrZSBhIGNhciwgb3IgZmFjdG9ycyBjYW4gYmUgb3JkZXJlZC4gT3JkZXJlZCBmYWN0b3JzIG1ha2Ugc2Vuc2UgZm9yIHRoaW5ncyB0aGF0IGhhdmUgbGV2ZWxzIGxpa2UgJ0hpZ2gnLCAnTWVkaXVtJywgYW5kICdMb3cnOyBvciAnRmlyc3QnLCAnU2Vjb25kJyBhbmQgJ1RoaXJkJywgZXRjLgpgYGB7cn0KZmFjX2IgPC0gZmFjdG9yKGMoIkdvb2QiLCAiQmV0dGVyIiwgIkdvb2QiLCAiQmVzdCIsICJCZXR0ZXIiKSwgbGV2ZWxzID0gYygiR29vZCIsICJCZXR0ZXIiLCAiQmVzdCIpLCAgb3JkZXJlZCA9IFRSVUUpCnN0cihmYWNfYikKYGBgCgpTbyB3aGF0IGhhcHBlbnMgaWYgd2UgdHJ5IHRvIGFkZCBhbiBlbGVtZW50IHRvIHRoZSBmYWN0b3I/CmBgYHtyfQp2ZWNfaSA8LSBjKGZhY19iLCBUUlVFKQpzdHIodmVjX2kpCmBgYApgYGB7cn0KdmVjX2ogPC0gYyhmYWNfYiwgIkJhZCIpCnN0cih2ZWNfaikKYGBgCgoKQmUgd2FybmVkIQoKIyMgQmFzaWMgT2JqZWN0IFR5cGVzCgpSIG9iamVjdHMgY2FuIGNvbWUgaW4gYSB2YXJpZXR5IG9mIHR5cGVzLiBUaGUgbW9zdCBjb21tb25seSB1c2VkIGFyZSBgdmVjdG9yc2AgKGluY2x1ZGluZyBgZmFjdG9yc2ApLCBgbWF0cmljZXNgLCBgZGF0YSBmcmFtZXNgIGFuZCBgbGlzdHNgLiBXZSBoYXZlIGNvdmVyZWQgdmVjdG9ycy4gTWF0cmljZXMgYXJlIHNpbWlsYXIgdG8gdmVjdG9ycyBpbiB0aGF0IHRoZXkgY2FuIG9ubHkgaW5jbHVkZSBhIHNpbmdsZSBkYXRhIGNsYXNzLCBidXQgYXJlIHR3byBkaW1lbnNpb25hbCwgaGF2aW5nIGJvdGggcm93cyBhbmQgY29sdW1ucy4KCkEgc2ltcGxlIG1hdHJpeDoKYGBge3J9Cm1hdF9hIDwtIG1hdHJpeCggYygnYScsJ2EnLCdiJywnYycsJ2InLCdhJyksIG5yb3cgPSAyLCBuY29sID0gMywgYnlyb3cgPSBUUlVFKQptYXRfYQpgYGAKCkNoYW5naW5nIHRoZSBgYnlyb3cgPWAgb3B0aW9uOgpgYGB7cn0KbWF0X2IgPC0gbWF0cml4KCBjKCdhJywnYScsJ2InLCdjJywnYicsJ2EnKSwgbnJvdyA9IDIsIG5jb2wgPSAzLCBieXJvdyA9IEZBTFNFKQptYXRfYgpgYGAKCkEgbnVtZXJpYyBtYXRyaXg6CmBgYHtyfQptYXRfYyA8LSBtYXRyaXgoMTo2LCBucm93ID0gMiwgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSkKbWF0X2MKYGBgCk11bHRpcGx5aW5nIGJ5IG1hdHJpY2VzOgpgYGB7cn0KbWF0X2QgPC0gbWF0cml4KDE6NiwgbnJvdyA9IDIsIG5jb2wgPSAzLCBieXJvdyA9IEZBTFNFKQptYXRfYyAqIG1hdF9kCmBgYAoKSWYgeW91IHdvcmsgd2l0aCBkYXRhIGZvcm1hdHRlZCBpbiBzcHJlYWRzaGVldHMsIGFuZCBJIHRoaW5rIG1vc3QgcGVvcGxlIGRvLCB5b3Ugd2lsbCBtb3N0IG9mdGVuIGtlZXAgeW91ciBkYXRhIGluIGEgZGF0YSBmcmFtZS4gRGF0YSBmcmFtZXMgYXJlIGdyZWF0IGJlY2F1c2UgdGhleSBjYW4gdGFrZSBtdWx0aXBsZSBkYXRhIGNsYXNzZXMgc3RvcmVkIGluIGRpZmZlcmVudCBjb2x1bW5zLiBSIGNvbWVzIHdpdGggc29tZSBleGFtcGxlIGRhdGFzZXRzLCBtb3N0IG9mIHdoaWNoIGFyZSBub3QgdmVyeSBpbnRlcmVzdGluZy4gWW91IHdpbGwgb2Z0ZW4gc2VlIHR1dG9yaWFscyBvbiB0aGUgd2ViIHRoYXQgdXNlIGVpdGhlciBgbXRjYXJzYCBvciBgaXJpc2AgaW4gdGhlaXIgZXhhbXBsZXMuIFRoZXNlIGRvIG5vdCBzaG93IHVwIGluIHlvdXIgZW52aXJvbm1lbnQgcGFuZSwgYnV0IHRoZXkgYXJlIHRoZXJlLiBMZXQncyBsb29rIGF0IHRoZSBgaXJpc2AgZGF0YS4gCgpGaXJzdCBsZXQncyBsb29rIGF0IHRoZSBkaW1lbnNpb25zIG9mIGBpcmlzYC4KYGBge3J9CmRpbShpcmlzKQpgYGAKSXQgaGFzIDE1MCByb3dzIGFuZCA1IGNvbHVtbnMuIFdlIHByb2JhYmx5IGRvIG5vdCB3YW50IHRvIGp1c3Qgb3V0cHV0IHRoZSAxNTAgcm93cyBpbnRvIG91ciBjb25zb2xlIHNvIHdlIGNhbiBnZXQgYSBsb29rIGF0IHRoZSBkYXRhIGluIGEgY291cGxlIG9mIHdheXMuIFdlIGNhbiBsb29rIGF0IHRoZSBgaGVhZCgpYCBvZiB0aGUgZGF0YSAob3IgZXZlbiBgdGFpbCgpYCkuCmBgYHtyfQpoZWFkKGlyaXMpCmBgYApUaGlzIGdpdmVzIHVzIHRoZSBmaXJzdCA2IHJvd3MgYXMgYSBkZWZhdWx0LiBgaGVhZChpcmlzLCA4KWAgd291bGQgZ2l2ZSB1cyB0aGUgZmlyc3QgOCByb3dzLiBJZiB3ZSBydW4gYHN0cigpYCBvbiBgaXJpc2Agd2Ugc2VlIHRoYXQgdGhlcmUgYXJlIGluZGVlZCBkaWZmZXJlbnQgZGF0YSBjbGFzc2VzIGluIG9uZSBvYmplY3QuIFRoZSBTcGVjaWVzIGNvbHVtbiBpcyBhIGZhY3Rvciwgd2hpbGUgYWxsIHRoZSBvdGhlciBjb2x1bW5zIGFyZSBudW1lcmljLgpgYGB7cn0Kc3RyKGlyaXMpCmBgYAoKCllvdSBjYW4gY3JlYXRlIGRhdGEgZnJhbWVzIHlvdXJzZWxmIGJ5IHVzaW5nIGBkYXRhLmZyYW1lKClgIG9uIHZlY3RvcnMgdGhhdCBoYXZlIGVxdWFsIGxlbmd0aC4KYGBge3J9CnZlY19hIDwtIDE6MTIgIyBUaGlzIGRlbGV0ZXMgb3VyIG9sZCB2ZWNfYSBwZXJtYW5lbnRseSBhbmQgd2l0aG91dCB3YXJuaW5nLgp2ZWNfYiA8LSBzYW1wbGUoMToxMDAsIHNpemUgPSAxMiwgcmVwbGFjZSA9IFRSVUUpICMgUmFuZG9tbHkgc2FtcGxpbmcgMTIgbnVtYmVycyBiZXR3ZWVuIDEgYW5kIDEwMCB3aXRoIHJlcGxhY2VtZW50IChtZWFuaW5nIHlvdSBjb3VsZCBnZXQgdGhlIG51bWJlciA4OCB0d2ljZSkuIFlvdSB3aWxsIGdldCBkaWZmZXJlbnQgbnVtYmVycyBldmVyeSB0aW1lIHlvdSBydW4gdGhpcy4KZmFjX2EgPC0gcmVwKGZhY19hLCB0aW1lcyA9IDQpICMgZmFjX2EgaGFkIGEgbGVuZ3RoKCkgb2YgMyBidXQgbm93IGl0IGlzIGxlbmd0aCAxMgpwZW9wbGVOYW1lcyA8LSByZXAocGVvcGxlTmFtZXMsIGVhY2ggPSAzKSAjIFdoYXQgaGFwcGVucyBpZiB5b3UgcnVuIHRoaXMgbGluZSBhbmQgdGhlIG9uZSBhYm92ZSBpdCBtdWx0aXBsZSB0aW1lcyAoRE9OJ1QgVFJZIElULikKCmRmX2EgPC0gZGF0YS5mcmFtZSh2ZWNfYSwgdmVjX2IsIGZhY19hLCBwZW9wbGVOYW1lcykKaGVhZChkZl9hKQpgYGAKClRoZXJlIGlzIGEgbG90IHRvIGtub3cgYWJvdXQgZGF0YSBmcmFtZXMsIGJ1dCB3ZSdsbCBzYXZlIHRoYXQgZm9yIHRoZSBSZWFkaW5nIERhdGEgaW50byBSIGFuZCBDbGVhbmluZyBEYXRhIHNlY3Rpb25zLiAKClRoZSBmaW5hbCBvYmplY3QgaXMgYSBsaXN0LiBMaXN0cyBjYW4gYmUgY29tcHJpc2VkIG9mIGFueXRoaW5nLiBUaGV5IGNhbiBiZSB2ZXJ5IHVzZWZ1bCwgYnV0IHdlIHdpbGwgbm90IHRhbGsgbXVjaCBhYm91dCB0aGVtIGhlcmUuIFRoZSBmb2xsb3dpbmcgY29kZSBjcmVhdGVzIGEgbGlzdCBvZiB0aGUgZGF0YSBmcmFtZXMgYG10Y2Fyc2AgYW5kIGBpcmlzYCwgYSBzaW5nbGUgY2hhcmFjdGVyIHZhbHVlLCBhIHNpbmdsZSBudW1lcmljIHZhbHVlLCBhIGxvZ2ljYWwgdmVjdG9yLCBhbmQgSSBldmVuIGluY2x1ZGVkIGEgY2hhcmFjdGVyIHN0cmluZyBmb3IgZ29vZCBtZWFzdXJlLgpgYGB7cn0KbGlzdF9hIDwtIGxpc3QobXRjYXJzLCBpcmlzLCAiYSIsIDEsIGMoVFJVRSwgRkFMU0UsIEZBTFNFKSwgInRoZSBraXRjaGVuIHNpbmsiKQpgYGAKCldlIHdpbGwgdGFsayBtb3JlIGFib3V0IGhvdyB0byBnZXQgdmFsdWVzIG91dCBvZiBsaXN0cywgZGF0YSBmcmFtZXMgYW5kIG1hdHJpY2VzIGxhdGVyLgoKIyMgV29ya2luZyBEaXJlY3RvcnkgYW5kIENyZWF0aW5nIGFuIFIgUHJvamVjdAoKV2hlbiB3b3JraW5nIHdpdGggUiBpdCBpcyBpbXBvcnRhbnQgdG8ga25vdyB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LiBZb3UgY2FuIGZpbmQgb3V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkgYnkgcnVubmluZzoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZ2V0d2QoKQpgYGAKCllvdSBjYW4gc2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkgYnkgaW5zZXJ0aW5nIGEgcGF0aG5hbWUgaW50byBgc2V0d2QoKWAsIG9yIGJ5IGRvaW5nIHRoaXMgaW4gUlN0dWRpbyBieSBjbGlja2luZyBvbiB0aGUgU2Vzc2lvbiBkcm9wZG93biBtZW51LgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpzZXR3ZCgiL1VzZXJzL2JyaWFuL0Ryb3Bib3gvZGJfZG9jdW1lbnRzL0hvbWVSd29yay9EYXRhQ2FycGVudHJ5IikKYGBgCgpBIGdvb2Qgd2F5IHRvIGtlZXAgeW91ciBSIHdvcmsgb3JnYW5pemVkLCBob3dldmVyLCBpcyB0byBjcmVhdGUgYW4gUiBQcm9qZWN0LiBQcm9qZWN0cyBhcmUgY3JlYXRlZCBpbiB0aGVpciBvd24gZm9sZGVyIGFuZCB3aWxsIGtlZXAgUiBzY3JpcHRzLCBhbmQgc2F2ZWQgcGxvdHMgb3IgZGF0YSB0aGVyZS4gWW91IGNhbiBjcmVhdGUgYSBuZXcgcHJvamVjdCBieSBjbGlja2luZyBvbmcgdGhlIFIgUHJvamVjdCBJY29uIGluIHRoZSB0b3AgcmlnaHQgY29ybmVyIG9mIHlvdXIgUlN0dWRpbyB3aW5kb3cgYW5kIHNlbGVjdGluZyAnTmV3IFByb2plY3QnLiBZb3UgY2FuIGNob29zZSBlaXRoZXIgdG8gY3JlYXRlIGEgbmV3IGRpcmVjdG9yeSAoZm9sZGVyKSBvciB0byB1c2UgYW4gZXhpc3Rpbmcgb25lLiBMZXQncyBhbGwgY3JlYXRlIGEgcHJvamVjdCBpbiBhIGZvbGRlciBjYWxsZWQgJ0RhdGFDYXJwZW50cnknLgoKIyMgQSBGZXcgVGlwcyBhbmQgVHJpY2tzIEdvaW5nIEZvcndhcmQKClRoZSBSU3R1ZGlvIElERSBoYXMgbWFueSB1c2VmdWwgY29tbWFuZHMgZm9yIG1ha2luZyBwcm9ncmFtbWluZyBpbiBSIGVhc2llci4gWW91IGNhbiBsZWFybiBhYm91dCBtb3JlIG9mIHRoZXNlIGJ5IGRvd25sb2FkaW5nIHRoZSBSU3R1ZGlvbiBjaGVhdHNoZWV0IGF0IDxodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvPi4gKFRoZSBvdGhlcnMgYXJlIGdyZWF0LCB0b28gLSBlc3BlY2lhbGx5IHRoZSBkYXRhIHZpcyBhbmQgZGF0YSB0cmFuc2Zvcm1hdGlvbiBvbmVzLikKClRyeSB0aGlzOiBDbGljayBvbiB0aGUgY29uc29sZSBhbmQgcHJlc3MgdGhlIHVwIGFycm93IG9uIHlvdXIga2V5Ym9hcmQuIEl0IGdpdmVzIHRoZSBsYXN0IGNvbW1hbmQgdGhhdCB5b3UgcmFuLiBQcmV0dHkgc3RhbmRhcmQgc3R1ZmYuIEhvd2V2ZXIsIGlmIHlvdSB3YW50IHRvIGtub3cgYWJvdXQgdGhlIGxhc3QgZmV3IHRpbWVzIHlvdSByYW4gYSBwYXJ0aWN1bGFyIGNvbW1hbmQsIG9yIGNyZWF0ZWQgY2VydGFpbiBvYmplY3RzIHR5cGUgYSBmZXcgbGV0dGVycyBhbmQgdGhlbiBwcmVzcyBgQ3RybGAgKFdpbmRvd3MpIG9yIGBDbWRgIChNYWMpIGFuZCBVcC4gTGV0J3Mgc2VlIHRoZSBjb2RlIHdlIHVzZWQgdG8gY3JlYXRlIHRoZSB2ZWN0b3JzLiBUeXBlIGB2ZWNgLCB0aGVuIGBDbWRgICsgVXAuIFRoaXMgaXMgZXh0cmVtZWx5IHVzZWZ1bC4gKFVubGVzcyBvdGhlcndpc2Ugbm90ZWQsIGlmIE1hYyBpcyBgQ21kYCwgV2luZG93cyBpcyBgQ3RybGAuKQoKU29tZSBvdGhlciB1c2VmdWwgc2hvcnRjdXRzOgoKMS4gYENtZGArYFNoaWZ0YCtgRmAgLSBMb29rIGZvciB0ZXh0IGluIGEgZm9sZGVyLgoyLiBgQ21kYCtgU2hpZnRgK2BDYCAtIENvbW1lbnRzIG9yIHVuY29tbWVudHMgYSBibG9jayBvZiBjb2RlLgozLiBgU2hpZnRgK2BBbHRgK2BVcC9Eb3duYCAoV2luKTsgYENtZGArYE9wdGlvbmArYFVwL0Rvd25gIChNYWMpIC0gQ29waWVzIHRoZSBwcmVzZW50IGxpbmUgb2YgY29kZSBhYm92ZSBvciBiZWxvdy4KNC4gYENtZGArYE9wdGlvbmArYENsaWNrYCAoVHJ5IGBTaGlmdGArYEFsdGArYENsaWNrYCBvbiBXaW5kb3dzKSAtIENyZWF0ZXMgbXVsdGlwbGUgY3Vyc29ycy4KCiMjIEEgV29yZCBhYm91dCBSIFNjcmlwdHMsIFVuZG8sIGFuZCBSZXByb2R1Y2FiaWxpdHkKCjEuIE9wZW4gYSBuZXcgUiBTY3JpcHQgKFRvcCBsZWZ0IGNvcm5lcikuIAoyLiBUeXBlOiBgbmV3VmVjIDwtIDE6MTJgLgozLiBXaXRoIHlvdXIgY3Vyc29yIGF0IHRoZSBlbmQgb2YgdGhhdCBsaW5lLCBwcmVzcyBgQ21kYCtgUmV0dXJuYC4KNC4gQ2hhbmdlIHRoZSBsaW5lIHlvdSB0eXBlZCB0bzogYG5ld1ZlYyA8LSAiT29wcyEiYAo1LiBSdW4gc3RlcCAzIGFnYWluLgo2LiBUcnkgdG8gdW5kby4KCldoYXQgaGFwcGVucz8gVGhpcyBpcyBvbmUgb2YgdGhlIGJlc3QgZmVhdHVyZXMgb2YgUiAob3Igc2ltaWxhciB3YXlzIG9mIGFuYWx5emluZyBkYXRhIGxpa2UgUHl0aG9uKS4gVGhpcyBiZWhhdmlvdXIgbWVhbnMgd2Ugc2hvdWxkIGFsdGVyIHRoZSB3YXkgbWFueSBvZiB1cyB0eXBpY2FsbHkgdXNlIHNvZnR3YXJlIGFuZCB0aGluayBhYm91dCBob3cgdG8gbWFrZSBvdXIgd29yayBlYXNpbHkgcmVwcm9kdWNpYmxlLiBXaGlsZSBtYW55IG5ldyB1c2VycyB0aGluayBvZiB0aGUgb2JqZWN0cyBhcyB0aGV5IGV4aXN0IHdpdGhpbiB0aGUgUiBFbnZpcm9ubWVudCBhcyB0aGUgaW1wb3J0YW50IG91dHB1dCBvZiB0aGVpciBsYWJvdXIgdGhpcyB2aWV3IGlzIGluY29ycmVjdC4gV2hhdCB5b3Ugc2hvdWxkIGJlIGFpbWluZyB0byBwcm9kdWNlIGlzIGEgcmVwcm9kdWNhYmxlIFIgc2NyaXB0LiBUaGlzIGRvZXMgbm90IG1lYW4gdGhhdCBtb3N0IFIgU2NyaXB0cyB3aWxsIG5vdCBjb250YWluIGEgYnVuY2ggb2Ygcm91Z2ggUiBjb2RlLiBJdCBtZWFucyB0aGF0IGF0IGxlYXN0IG9uZSBvZiB5b3VyIG9wZW4gc2NyaXB0cyBzaG91bGQgY29udGFpbiBnb29kIHdvcmsgdGhhdCBjb250YWlucyBsb3RzIG9mIGNvbW1lbnRzIHRoYXQgd291bGQgaGVscCB5b3UgKG9yIHNvbWVvbmUgZWxzZSkgaWYgeW91IGNhbWUgYmFjayB0byBpdCBzaXggbW9udGhzIGxhdGVyLiBUaGUgY29tbWFuZCBgcm0obGlzdCA9IGxzKCkpYCByZW1vdmVzIGV2ZXJ5dGhpbmcgaW4geW91ciBSIEVudmlyb25tZW50IChidXQgbm90IHlvdXIgSGlzdG9yeSkuIElmIHlvdSBhcmUgZG9pbmcgdGhpbmdzIGNvcnJlY3RseSwgeW91IGhhdmUgYSBzY3JpcHQgdGhhdCB5b3UgY291bGQgc2ltcGx5ICdzb3VyY2UnIGFuZCBnZXQgZXZlcnl0aGluZyBiYWNrIGFnYWluIGlmIGV2ZXJ5dGhpbmcgd2VyZSB0byBiZSBkZWxldGVkLiBXb3JraW5nIHRoaXMgd2F5IGlzIHRoZSBpZGVhbC4gSSBkbyBub3QgYWx3YXlzIGZ1bGx5IGRvIHRoaXMsIGFuZCBzb21lIGRhdGFzZXRzIG9yIG1vZGVscyB0YWtlIGEgbG9vb29uZyB0aW1lIHRvIGxvYWQgYW5kIHJ1biBzbyBqdXN0IHJlLXJ1bm5pbmcgYSBzY3JpcHQgY2FuIHNvbWV0aW1lIGJlIGltcHJhY3RpY2FsLCBidXQgdGhlIHBvaW50IGhlcmUgaXMgdGhhdCB5b3Ugc2hvdWxkIG5vdCBnZXQgdG9vIGF0dGFjaGVkIHRvIG9iamVjdHMgaW4geW91ciBlbnZpcm9ubWVudCwgYnV0IHJhdGhlciBwYXkgYXR0ZW50aW9uIHRvIHRoZSBjb2RlIHRoYXQgY3JlYXRlZCB0aGVtLgoKIyMgVXAgTmV4dDogUmVhZGluZyBJbiBhbmQgQ2xlYW5pbmcgRGF0YQo=