Data frames are a structure that can hold many different data types in one simple structure.

Data frames are the lingua franca for R, especially once we start getting into more complicated analysis and manipulation. For simplicity, one can consider a data.frame object much like a spreadsheet. Each row represents a record on some object and each column—consisting of different kinds of data—are measurements on that object.

By in large, you will load your data into these structures and pass them to functions for plotting, simulation, plotting, etc. It is a good idea to get used to these.

Creating data.frame Objects

To make a data.frame we need to either:

  1. Make it from scratch using vectors (the more complicated).
  2. Load it from a file (perhaps the easiest way).

We shall start with the most complicated case.

Creating data.frame Objects de novo

site <- c( "Const","ESan", "Aqu")
longitude <- c( -111.675, -110.3686, -110.1043)
latitude <- c(25.0247, 24.45879, 23.2855)

sites <- data.frame( Site = site,
                     Longitude = longitude,
                     Latitude = latitude )
sites

The thing about data.frame objects is that the know how to organize and summarize their component variables.

summary(sites)
     Site             Longitude         Latitude    
 Length:3           Min.   :-111.7   Min.   :23.29  
 Class :character   1st Qu.:-111.0   1st Qu.:23.87  
 Mode  :character   Median :-110.4   Median :24.46  
                    Mean   :-110.7   Mean   :24.26  
                    3rd Qu.:-110.2   3rd Qu.:24.74  
                    Max.   :-110.1   Max.   :25.02  

Notice that each variable is summarized a way that gives as much information about the contnets of that variable. The summary() function is critical and one we will use over and over again.

Loading from File and/or URL

You can also load a data.frame in using a local file or a URL to a file somewhere on the internet. The easiest way to do this is to have your file as a CSV (Comma-Separated-Value) format. This is usually a \(File \to Save\;As\) kind of routine. It is important for reproducibility and collaboration to 1) Have only one version of your data set, and 2) Keep it in a format that is accessible such as plain text.

Here is the full version of those data loaded above. It is located on a Github website and is accessible to anyone with an internet connection.

url <- "https://raw.githubusercontent.com/dyerlab/ENVS-Lectures/master/data/arapat.csv"

Once we have the url or file as a character object, we can use the built-in read.csv() function to load the data into a local variable. Here is what that looks like.

Samples <- read.csv(url)
summary( Samples )
   Stratum            Longitude         Latitude    
 Length:39          Min.   :-114.3   Min.   :23.08  
 Class :character   1st Qu.:-112.9   1st Qu.:24.52  
 Mode  :character   Median :-111.5   Median :26.21  
                    Mean   :-111.7   Mean   :26.14  
                    3rd Qu.:-110.4   3rd Qu.:27.47  
                    Max.   :-109.1   Max.   :29.33  

Working with data.frame Objects

A variable representing a data.frame has properties and constituents just like a vector. The size of the data.frame is defined as the dimensions of it and represents the number of rows and columns it contains.

nrow( Samples )
[1] 39
ncol( Samples )
[1] 3

or use the dim() function which returns both of them.

dim( Samples )
[1] 39  3

Each variable within the data.frame is represented by the name we gave it when we added it to the data.frame.

names( Samples )
[1] "Stratum"   "Longitude" "Latitude" 
Stratum

Longitude

Latitude

The individual elements are available using the square bracket notation, just like for vectors with the exception that we have two coordinates instead of just one. The first coordinate is for row and the second is for column. So to grab the 10\(^{th}\) Stratum name (row), which is the 1\(^{st}\) variable (column) in the data.frame, we use:

Samples[10,1]
[1] "SFr"
SFr

While this is OK, it assumes we remember that the Stratum variable is the first variable in the data.frame. If you have data sets with thousands of columns of data in them, it becomes less-than-optimal to use variable numbers to index, which is why we can use the variable name itself in the following format.

Samples$Stratum[10]
[1] "SFr"
SFr

Here, we use the name of the data.frame connected to the name of the variable using the dollar sign ($) to hook them up.

For readability, it is advised that you use the dollar-sign notation to get and set values within a data.frame. It will always be more clear to you and anyone who reads your code if you are as verbose as you can be in your code. Trust me, your future self will like you much better if you adopt this habit as soon as possible.

Similar to for vectors, we can grab the head and tail of the whole data frame.

head( Samples )
tail(Samples, n=4)

We can use the same approach for setting values within a data.frame. Here I’ll expand the SFr notion to represent the full location name.

Samples$Stratum[10] <- "San Francisquito"
head( Samples, n=10)

ALERT: If you received an error message with some like

Warning message:
In `[<-.factor`(`*tmp*`, 10, value = c(30L, 32L, 29L, 19L, 20L,  :
  invalid factor level, NA generated

It is because the read.csv() function has a default value that converts any column with a character variable type into a factor data type… This is a stupid default option and you should change the default value in your R options file - To fix this issue, see r environment for or remember that every time you load in data, you must add the optional argument stringsAsFactors=FALSE to the read.csv() function call.

Samples <- read.csv(url, stringAsFactors=FALSE)

Run that and try to replace the Stratum name as above.

Slices

Just like for vectors, we can also take slices of components from data.frame objects. However, what we get in return depends on specifically what we ask for.

For example, taking from a single column in the data.frame returns a slice of that particular variable type.

x <- Samples[1:3, 1]
x
[1] "88" "9"  "84"
88

9

84
class(x)
[1] "character"
character

This is because all the returned values are from a single data column, which by definition must be the same kind of data.

You can ask R to return it as a data.frame rather than just a vector (in case you need to have a data.frame to work with) by passing an optional argument to the square brackets. You will notice it retains the row numbers.

x1 <- Samples[ 1:3, 1, drop=FALSE]
x1
class(x1)
[1] "data.frame"
data.frame

However, if we take the same number of object but from a row instead of a column, we can only get a data.frame object in return.

y <- Samples[1, 1:3]
y
class(y)
[1] "data.frame"
data.frame

because this time, we are asking for the three variables representing the first record. Because these values are being drawn from across different columns of data, R does not concatenate these into a single vector and coearce them to the least common data type. Rather it makes a new data.frame object for you and returns that.

Modifying Existing Data Frames

Once we have a data.frame in memory, we can also add to it. If we are adding columns, we can append a single varible onto it and R will put it on the far right side of the data.frame.

Samples$ID <- 1:nrow(Samples)
summary( Samples )
   Stratum            Longitude         Latitude           ID      
 Length:39          Min.   :-114.3   Min.   :23.08   Min.   : 1.0  
 Class :character   1st Qu.:-112.9   1st Qu.:24.52   1st Qu.:10.5  
 Mode  :character   Median :-111.5   Median :26.21   Median :20.0  
                    Mean   :-111.7   Mean   :26.14   Mean   :20.0  
                    3rd Qu.:-110.4   3rd Qu.:27.47   3rd Qu.:29.5  
                    Max.   :-109.1   Max.   :29.33   Max.   :39.0  

If we want to add rows to the data.frame, it is a bit more involved because we are going to have to either a list object whose variables are of the same order as the original data.frame or anoher entire data.frame (whose names are identical to the original)

names(Samples)
[1] "Stratum"   "Longitude" "Latitude"  "ID"       
Stratum

Longitude

Latitude

ID

To add a single row, we use rbind() and pass it the data.frame and a new list object.

Samples <- rbind( Samples, list("Los Cabos",-109.7124, 23.0799, 40))
tail(Samples)

You can also use another data.frame, which in this case must have the same named varaibles as the original.

moresites <- data.frame( ID = 41:42,
                         Stratum = c("Los Barriles","Comondu"),
                         Longitude = c(-109.7026, -111.8442),
                         Latitude = c(23.6811, 26.0708) 
                         )
names(moresites)
[1] "ID"        "Stratum"   "Longitude" "Latitude" 
ID

Stratum

Longitude

Latitude

However, notice that the order in which these varaibles in moresites is different than those in Samples. The rbind() function rearranges the order when it concatenates onto the rows.

Samples <- rbind( Samples, moresites)
tail( Samples )

To delete a column of data, assign it the value of NULL.

Samples$ID <- NULL
summary(Samples)
   Stratum            Longitude         Latitude    
 Length:42          Min.   :-114.3   Min.   :23.08  
 Class :character   1st Qu.:-112.8   1st Qu.:24.28  
 Mode  :character   Median :-111.5   Median :26.04  
                    Mean   :-111.6   Mean   :26.01  
                    3rd Qu.:-110.3   3rd Qu.:27.39  
                    Max.   :-109.1   Max.   :29.33  

or use negative indices to delete rows.

Samples <- Samples[ -42:-40, ]
summary(Samples)
   Stratum            Longitude         Latitude    
 Length:39          Min.   :-114.3   Min.   :23.08  
 Class :character   1st Qu.:-112.9   1st Qu.:24.52  
 Mode  :character   Median :-111.5   Median :26.21  
                    Mean   :-111.7   Mean   :26.14  
                    3rd Qu.:-110.4   3rd Qu.:27.47  
                    Max.   :-109.1   Max.   :29.33  

The data.frame object is a fundamental component of your R work flow and being able to manipulate data within it and extract data from it are a huge part of becoming data literate.

LS0tCnRpdGxlOiAiRGF0YSBGcmFtZXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIGNzczogImVudnM1NDMtc3R5bGVzLmNzcyIKLS0tCgo+IERhdGEgZnJhbWVzIGFyZSBhIHN0cnVjdHVyZSB0aGF0IGNhbiBob2xkIG1hbnkgZGlmZmVyZW50IGRhdGEgdHlwZXMgaW4gb25lIHNpbXBsZSBzdHJ1Y3R1cmUuCgpEYXRhIGZyYW1lcyBhcmUgdGhlICpsaW5ndWEgZnJhbmNhKiBmb3IgYFJgLCBlc3BlY2lhbGx5IG9uY2Ugd2Ugc3RhcnQgZ2V0dGluZyBpbnRvIG1vcmUgY29tcGxpY2F0ZWQgYW5hbHlzaXMgYW5kIG1hbmlwdWxhdGlvbi4gIEZvciBzaW1wbGljaXR5LCBvbmUgY2FuIGNvbnNpZGVyIGEgYGRhdGEuZnJhbWVgIG9iamVjdCBtdWNoIGxpa2UgYSBzcHJlYWRzaGVldC4gIEVhY2ggcm93IHJlcHJlc2VudHMgYSByZWNvcmQgb24gc29tZSBvYmplY3QgYW5kIGVhY2ggY29sdW1u4oCUY29uc2lzdGluZyBvZiBkaWZmZXJlbnQga2luZHMgb2YgZGF0YeKAlGFyZSBtZWFzdXJlbWVudHMgb24gdGhhdCBvYmplY3QuICAKCkJ5IGluIGxhcmdlLCB5b3Ugd2lsbCBsb2FkIHlvdXIgZGF0YSBpbnRvIHRoZXNlIHN0cnVjdHVyZXMgYW5kIHBhc3MgdGhlbSB0byBmdW5jdGlvbnMgZm9yIHBsb3R0aW5nLCBzaW11bGF0aW9uLCBwbG90dGluZywgZXRjLiAgSXQgaXMgYSBnb29kIGlkZWEgdG8gZ2V0IHVzZWQgdG8gdGhlc2UuCgoKIyMgQ3JlYXRpbmcgYGRhdGEuZnJhbWVgIE9iamVjdHMKClRvIG1ha2UgYSBgZGF0YS5mcmFtZWAgd2UgbmVlZCB0byBlaXRoZXI6CgogIDEuIE1ha2UgaXQgZnJvbSBzY3JhdGNoIHVzaW5nIHZlY3RvcnMgKHRoZSBtb3JlIGNvbXBsaWNhdGVkKS4KICAyLiBMb2FkIGl0IGZyb20gYSBmaWxlIChwZXJoYXBzIHRoZSBlYXNpZXN0IHdheSkuCgpXZSBzaGFsbCBzdGFydCB3aXRoIHRoZSBtb3N0IGNvbXBsaWNhdGVkIGNhc2UuICAKCiMjIyBDcmVhdGluZyBgZGF0YS5mcmFtZWAgT2JqZWN0cyAqZGUgbm92byoKCmBgYHtyfQpzaXRlIDwtIGMoICJDb25zdCIsIkVTYW4iLCAiQXF1IikKbG9uZ2l0dWRlIDwtIGMoIC0xMTEuNjc1LCAtMTEwLjM2ODYsIC0xMTAuMTA0MykKbGF0aXR1ZGUgPC0gYygyNS4wMjQ3LCAyNC40NTg3OSwgMjMuMjg1NSkKCnNpdGVzIDwtIGRhdGEuZnJhbWUoIFNpdGUgPSBzaXRlLAogICAgICAgICAgICAgICAgICAgICBMb25naXR1ZGUgPSBsb25naXR1ZGUsCiAgICAgICAgICAgICAgICAgICAgIExhdGl0dWRlID0gbGF0aXR1ZGUgKQpzaXRlcwpgYGAKClRoZSB0aGluZyBhYm91dCBgZGF0YS5mcmFtZWAgb2JqZWN0cyBpcyB0aGF0IHRoZSBrbm93IGhvdyB0byBvcmdhbml6ZSBhbmQgc3VtbWFyaXplIHRoZWlyIGNvbXBvbmVudCB2YXJpYWJsZXMuCgpgYGB7cn0Kc3VtbWFyeShzaXRlcykKYGBgCgpOb3RpY2UgdGhhdCBlYWNoIHZhcmlhYmxlIGlzIHN1bW1hcml6ZWQgYSB3YXkgdGhhdCBnaXZlcyBhcyBtdWNoIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjb250bmV0cyBvZiB0aGF0IHZhcmlhYmxlLiBUaGUgYHN1bW1hcnkoKWAgZnVuY3Rpb24gaXMgY3JpdGljYWwgYW5kIG9uZSB3ZSB3aWxsIHVzZSBvdmVyIGFuZCBvdmVyIGFnYWluLgoKIyMjIExvYWRpbmcgZnJvbSBGaWxlIGFuZC9vciBVUkwKCllvdSBjYW4gYWxzbyBsb2FkIGEgYGRhdGEuZnJhbWVgIGluIHVzaW5nIGEgbG9jYWwgZmlsZSBvciBhIFVSTCB0byBhIGZpbGUgc29tZXdoZXJlIG9uIHRoZSBpbnRlcm5ldC4gIFRoZSBlYXNpZXN0IHdheSB0byBkbyB0aGlzIGlzIHRvIGhhdmUgeW91ciBmaWxlIGFzIGEgQ1NWIChDb21tYS1TZXBhcmF0ZWQtVmFsdWUpIGZvcm1hdC4gIFRoaXMgaXMgdXN1YWxseSBhICRGaWxlIFx0byBTYXZlXDtBcyQga2luZCBvZiByb3V0aW5lLiAgSXQgaXMgaW1wb3J0YW50IGZvciByZXByb2R1Y2liaWxpdHkgYW5kIGNvbGxhYm9yYXRpb24gdG8gMSkgSGF2ZSBvbmx5IG9uZSB2ZXJzaW9uIG9mIHlvdXIgZGF0YSBzZXQsIGFuZCAyKSBLZWVwIGl0IGluIGEgZm9ybWF0IHRoYXQgaXMgYWNjZXNzaWJsZSBzdWNoIGFzIHBsYWluIHRleHQuCgpIZXJlIGlzIHRoZSBmdWxsIHZlcnNpb24gb2YgdGhvc2UgZGF0YSBsb2FkZWQgYWJvdmUuICBJdCBpcyBsb2NhdGVkIG9uIGEgW0dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2R5ZXJsYWIvRU5WUy1MZWN0dXJlcykgd2Vic2l0ZSBhbmQgaXMgYWNjZXNzaWJsZSB0byBhbnlvbmUgd2l0aCBhbiBpbnRlcm5ldCBjb25uZWN0aW9uLgoKYGBge3J9CnVybCA8LSAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2R5ZXJsYWIvRU5WUy1MZWN0dXJlcy9tYXN0ZXIvZGF0YS9hcmFwYXQuY3N2IgpgYGAKCk9uY2Ugd2UgaGF2ZSB0aGUgdXJsIG9yIGZpbGUgYXMgYSBgY2hhcmFjdGVyYCBvYmplY3QsIHdlIGNhbiB1c2UgdGhlIGJ1aWx0LWluIGByZWFkLmNzdigpYCBmdW5jdGlvbiB0byBsb2FkIHRoZSBkYXRhIGludG8gYSBsb2NhbCB2YXJpYWJsZS4gIEhlcmUgaXMgd2hhdCB0aGF0IGxvb2tzIGxpa2UuCgpgYGB7cn0KU2FtcGxlcyA8LSByZWFkLmNzdih1cmwpCnN1bW1hcnkoIFNhbXBsZXMgKQpgYGAKCgoKIyMgV29ya2luZyB3aXRoIGBkYXRhLmZyYW1lYCBPYmplY3RzCgpBIHZhcmlhYmxlIHJlcHJlc2VudGluZyBhIGBkYXRhLmZyYW1lYCBoYXMgcHJvcGVydGllcyBhbmQgY29uc3RpdHVlbnRzIGp1c3QgbGlrZSBhIHZlY3Rvci4gIFRoZSBzaXplIG9mIHRoZSBgZGF0YS5mcmFtZWAgaXMgZGVmaW5lZCBhcyB0aGUgYGRpbWBlbnNpb25zIG9mIGl0IGFuZCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgKnJvd3MqIGFuZCAqY29sdW1ucyogaXQgY29udGFpbnMuCgpgYGB7cn0KbnJvdyggU2FtcGxlcyApCm5jb2woIFNhbXBsZXMgKQpgYGAKCm9yIHVzZSB0aGUgYGRpbSgpYCBmdW5jdGlvbiB3aGljaCByZXR1cm5zIGJvdGggb2YgdGhlbS4KCmBgYHtyfQpkaW0oIFNhbXBsZXMgKQpgYGAKCgpFYWNoIHZhcmlhYmxlIHdpdGhpbiB0aGUgYGRhdGEuZnJhbWVgIGlzIHJlcHJlc2VudGVkIGJ5IHRoZSBuYW1lIHdlIGdhdmUgaXQgd2hlbiB3ZSBhZGRlZCBpdCB0byB0aGUgYGRhdGEuZnJhbWVgLgoKYGBge3J9Cm5hbWVzKCBTYW1wbGVzICkKYGBgCgpUaGUgaW5kaXZpZHVhbCBlbGVtZW50cyBhcmUgYXZhaWxhYmxlIHVzaW5nIHRoZSBzcXVhcmUgYnJhY2tldCBub3RhdGlvbiwganVzdCBsaWtlIGZvciBbdmVjdG9yc10oLi4vdmVjdG9ycy9uYXJyaWF0aXZlLm5iLmh0bWwpICp3aXRoIHRoZSBleGNlcHRpb24gdGhhdCogd2UgaGF2ZSB0d28gY29vcmRpbmF0ZXMgaW5zdGVhZCBvZiBqdXN0IG9uZS4gIFRoZSBmaXJzdCBjb29yZGluYXRlIGlzIGZvciAqcm93KiBhbmQgdGhlIHNlY29uZCBpcyBmb3IgKmNvbHVtbiouICBTbyB0byBncmFiIHRoZSAxMCRee3RofSQgYFN0cmF0dW1gIG5hbWUgKHJvdyksIHdoaWNoIGlzIHRoZSAxJF57c3R9JCB2YXJpYWJsZSAoY29sdW1uKSBpbiB0aGUgYGRhdGEuZnJhbWVgLCB3ZSB1c2U6CgpgYGB7cn0KU2FtcGxlc1sxMCwxXQpgYGAKCldoaWxlIHRoaXMgaXMgT0ssIGl0IGFzc3VtZXMgd2UgcmVtZW1iZXIgdGhhdCB0aGUgYFN0cmF0dW1gIHZhcmlhYmxlIGlzIHRoZSBmaXJzdCB2YXJpYWJsZSBpbiB0aGUgYGRhdGEuZnJhbWVgLiAgSWYgeW91IGhhdmUgZGF0YSBzZXRzIHdpdGggdGhvdXNhbmRzIG9mIGNvbHVtbnMgb2YgZGF0YSBpbiB0aGVtLCBpdCBiZWNvbWVzIGxlc3MtdGhhbi1vcHRpbWFsIHRvIHVzZSB2YXJpYWJsZSBudW1iZXJzIHRvIGluZGV4LCB3aGljaCBpcyB3aHkgd2UgY2FuIHVzZSB0aGUgdmFyaWFibGUgbmFtZSBpdHNlbGYgaW4gdGhlIGZvbGxvd2luZyBmb3JtYXQuCgpgYGB7cn0KU2FtcGxlcyRTdHJhdHVtWzEwXQpgYGAKCkhlcmUsIHdlIHVzZSB0aGUgbmFtZSBvZiB0aGUgYGRhdGEuZnJhbWVgIGNvbm5lY3RlZCB0byB0aGUgbmFtZSBvZiB0aGUgdmFyaWFibGUgdXNpbmcgdGhlIGRvbGxhciBzaWduIChcJCkgdG8gaG9vayB0aGVtIHVwLiAgCgo8ZGl2IGNsYXNzPSJib3gteWVsbG93Ij5Gb3IgcmVhZGFiaWxpdHksIGl0IGlzIGFkdmlzZWQgdGhhdCB5b3UgdXNlIHRoZSBkb2xsYXItc2lnbiBub3RhdGlvbiB0byBnZXQgYW5kIHNldCB2YWx1ZXMgd2l0aGluIGEgYGRhdGEuZnJhbWVgLiAgSXQgd2lsbCBhbHdheXMgYmUgbW9yZSBjbGVhciB0byB5b3UgYW5kIGFueW9uZSB3aG8gcmVhZHMgeW91ciBjb2RlIGlmIHlvdSBhcmUgYXMgdmVyYm9zZSBhcyB5b3UgY2FuIGJlIGluIHlvdXIgY29kZS4gIFRydXN0IG1lLCB5b3VyICpmdXR1cmUgc2VsZiogd2lsbCBsaWtlIHlvdSBtdWNoIGJldHRlciBpZiB5b3UgYWRvcHQgdGhpcyBoYWJpdCBhcyBzb29uIGFzIHBvc3NpYmxlLjwvZGl2PgoKClNpbWlsYXIgdG8gZm9yIFt2ZWN0b3JzXSguLi92ZWN0b3JzL25hcnJpYXRpdmUubmIuaHRtbCksIHdlIGNhbiBncmFiIHRoZSBoZWFkIGFuZCB0YWlsIG9mIHRoZSB3aG9sZSBkYXRhIGZyYW1lLgoKYGBge3J9CmhlYWQoIFNhbXBsZXMgKQpgYGAKCmBgYHtyfQp0YWlsKFNhbXBsZXMsIG49NCkKYGBgCgoKV2UgY2FuIHVzZSB0aGUgc2FtZSBhcHByb2FjaCBmb3Igc2V0dGluZyB2YWx1ZXMgd2l0aGluIGEgYGRhdGEuZnJhbWVgLiAgSGVyZSBJJ2xsIGV4cGFuZCB0aGUgKlNGciogbm90aW9uIHRvIHJlcHJlc2VudCB0aGUgZnVsbCBsb2NhdGlvbiBuYW1lLgoKYGBge3J9ClNhbXBsZXMkU3RyYXR1bVsxMF0gPC0gIlNhbiBGcmFuY2lzcXVpdG8iCmhlYWQoIFNhbXBsZXMsIG49MTApCmBgYAoKPGRpdiBjbGFzcz0iYm94LXJlZCI+CioqQUxFUlQ6KiogIElmIHlvdSByZWNlaXZlZCBhbiBlcnJvciBtZXNzYWdlIHdpdGggc29tZSBsaWtlIApgYGAKV2FybmluZyBtZXNzYWdlOgpJbiBgWzwtLmZhY3RvcmAoYCp0bXAqYCwgMTAsIHZhbHVlID0gYygzMEwsIDMyTCwgMjlMLCAxOUwsIDIwTCwgIDoKICBpbnZhbGlkIGZhY3RvciBsZXZlbCwgTkEgZ2VuZXJhdGVkCmBgYApJdCBpcyBiZWNhdXNlIHRoZSBgcmVhZC5jc3YoKWAgZnVuY3Rpb24gaGFzIGEgZGVmYXVsdCB2YWx1ZSB0aGF0IGNvbnZlcnRzIGFueSBjb2x1bW4gd2l0aCBhIGBjaGFyYWN0ZXJgIHZhcmlhYmxlIHR5cGUgaW50byBhIGBmYWN0b3JgIGRhdGEgdHlwZS4uLiAgVGhpcyBpcyBhICoqc3R1cGlkKiogZGVmYXVsdCBvcHRpb24gYW5kIHlvdSBzaG91bGQgY2hhbmdlIHRoZSBkZWZhdWx0IHZhbHVlIGluIHlvdXIgYFJgIG9wdGlvbnMgZmlsZSAtIFRvIGZpeCB0aGlzIGlzc3VlLCBzZWUgW3IgZW52aXJvbm1lbnRdKC4uL2Vudmlyb25tZW50L25hcnJpYXRpdmUubmIuaHRtbCkgZm9yIG9yIHJlbWVtYmVyIHRoYXQgKmV2ZXJ5IHRpbWUgeW91IGxvYWQgaW4gZGF0YSosIHlvdSBtdXN0IGFkZCB0aGUgb3B0aW9uYWwgYXJndW1lbnQgYHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0VgIHRvIHRoZSBgcmVhZC5jc3YoKWAgZnVuY3Rpb24gY2FsbC4KCmBgYHtyIGV2YWw9RkFMU0V9ClNhbXBsZXMgPC0gcmVhZC5jc3YodXJsLCBzdHJpbmdBc0ZhY3RvcnM9RkFMU0UpCmBgYAoKUnVuIHRoYXQgYW5kIHRyeSB0byByZXBsYWNlIHRoZSBTdHJhdHVtIG5hbWUgYXMgYWJvdmUuCjwvZGl2PgoKCgojIyMgU2xpY2VzCgpKdXN0IGxpa2UgZm9yIFt2ZWN0b3JzXSguLi92ZWN0b3JzL25hcnJpYXRpdmUubmIuaHRtbCksIHdlIGNhbiBhbHNvIHRha2Ugc2xpY2VzIG9mIGNvbXBvbmVudHMgZnJvbSBgZGF0YS5mcmFtZWAgb2JqZWN0cy4gIEhvd2V2ZXIsIHdoYXQgd2UgZ2V0IGluIHJldHVybiBkZXBlbmRzIG9uIHNwZWNpZmljYWxseSB3aGF0IHdlIGFzayBmb3IuICAKCkZvciBleGFtcGxlLCB0YWtpbmcgZnJvbSBhIHNpbmdsZSBjb2x1bW4gaW4gdGhlIGBkYXRhLmZyYW1lYCByZXR1cm5zIGEgc2xpY2Ugb2YgdGhhdCBwYXJ0aWN1bGFyIHZhcmlhYmxlIHR5cGUuCgpgYGB7cn0KeCA8LSBTYW1wbGVzWzE6MywgMV0KeApjbGFzcyh4KQpgYGAKClRoaXMgaXMgYmVjYXVzZSBhbGwgdGhlIHJldHVybmVkIHZhbHVlcyBhcmUgZnJvbSBhIHNpbmdsZSBkYXRhIGNvbHVtbiwgd2hpY2ggYnkgZGVmaW5pdGlvbiAqbXVzdCogYmUgdGhlIHNhbWUga2luZCBvZiBkYXRhLiAgCgpZb3UgY2FuIGFzayBgUmAgdG8gcmV0dXJuIGl0IGFzIGEgYGRhdGEuZnJhbWVgIHJhdGhlciB0aGFuIGp1c3QgYSB2ZWN0b3IgKGluIGNhc2UgeW91IG5lZWQgdG8gaGF2ZSBhIGRhdGEuZnJhbWUgdG8gd29yayB3aXRoKSBieSBwYXNzaW5nIGFuIG9wdGlvbmFsIGFyZ3VtZW50IHRvIHRoZSBzcXVhcmUgYnJhY2tldHMuICBZb3Ugd2lsbCBub3RpY2UgaXQgcmV0YWlucyB0aGUgcm93IG51bWJlcnMuCgpgYGB7cn0KeDEgPC0gU2FtcGxlc1sgMTozLCAxLCBkcm9wPUZBTFNFXQp4MQpjbGFzcyh4MSkKYGBgCgoKSG93ZXZlciwgaWYgd2UgdGFrZSB0aGUgc2FtZSBudW1iZXIgb2Ygb2JqZWN0IGJ1dCBmcm9tIGEgcm93IGluc3RlYWQgb2YgYSBjb2x1bW4sIHdlIGNhbiAqb25seSogZ2V0IGEgYGRhdGEuZnJhbWVgIG9iamVjdCBpbiByZXR1cm4uCgpgYGB7cn0KeSA8LSBTYW1wbGVzWzEsIDE6M10KeQpjbGFzcyh5KQpgYGAKCmJlY2F1c2UgdGhpcyB0aW1lLCB3ZSBhcmUgYXNraW5nIGZvciB0aGUgdGhyZWUgdmFyaWFibGVzIHJlcHJlc2VudGluZyB0aGUgZmlyc3QgcmVjb3JkLiAgQmVjYXVzZSB0aGVzZSB2YWx1ZXMgYXJlIGJlaW5nIGRyYXduIGZyb20gYWNyb3NzIGRpZmZlcmVudCBjb2x1bW5zIG9mIGRhdGEsIGBSYCBkb2VzIG5vdCBjb25jYXRlbmF0ZSB0aGVzZSBpbnRvIGEgc2luZ2xlIHZlY3RvciBhbmQgY29lYXJjZSB0aGVtIHRvIHRoZSBsZWFzdCBjb21tb24gZGF0YSB0eXBlLiBSYXRoZXIgaXQgbWFrZXMgYSBuZXcgYGRhdGEuZnJhbWVgIG9iamVjdCBmb3IgeW91IGFuZCByZXR1cm5zIHRoYXQuCgoKIyMjIE1vZGlmeWluZyBFeGlzdGluZyBEYXRhIEZyYW1lcwoKT25jZSB3ZSBoYXZlIGEgYGRhdGEuZnJhbWVgIGluIG1lbW9yeSwgd2UgY2FuIGFsc28gYWRkIHRvIGl0LiAgSWYgd2UgYXJlIGFkZGluZyBjb2x1bW5zLCB3ZSBjYW4gYXBwZW5kIGEgc2luZ2xlIHZhcmlibGUgb250byBpdCBhbmQgYFJgIHdpbGwgcHV0IGl0IG9uIHRoZSBmYXIgcmlnaHQgc2lkZSBvZiB0aGUgYGRhdGEuZnJhbWVgLgoKYGBge3J9ClNhbXBsZXMkSUQgPC0gMTpucm93KFNhbXBsZXMpCnN1bW1hcnkoIFNhbXBsZXMgKQpgYGAKCgpJZiB3ZSB3YW50IHRvIGFkZCByb3dzIHRvIHRoZSBgZGF0YS5mcmFtZWAsIGl0IGlzIGEgYml0IG1vcmUgaW52b2x2ZWQgYmVjYXVzZSB3ZSBhcmUgZ29pbmcgdG8gaGF2ZSB0byBlaXRoZXIgYSBsaXN0IG9iamVjdCB3aG9zZSB2YXJpYWJsZXMgYXJlIG9mIHRoZSBzYW1lIG9yZGVyIGFzIHRoZSBvcmlnaW5hbCBgZGF0YS5mcmFtZWAgb3IgYW5vaGVyIGVudGlyZSBgZGF0YS5mcmFtZWAgKHdob3NlIG5hbWVzIGFyZSBpZGVudGljYWwgdG8gdGhlIG9yaWdpbmFsKQoKYGBge3J9Cm5hbWVzKFNhbXBsZXMpCmBgYAoKVG8gYWRkIGEgc2luZ2xlIHJvdywgd2UgdXNlIGByYmluZCgpYCBhbmQgcGFzcyBpdCB0aGUgYGRhdGEuZnJhbWVgIGFuZCBhIG5ldyBbbGlzdF0oLi4vbGlzdHMvbmFycmlhdGl2ZS5uYi5odG1sKSBvYmplY3QuCgpgYGB7cn0KU2FtcGxlcyA8LSByYmluZCggU2FtcGxlcywgbGlzdCgiTG9zIENhYm9zIiwtMTA5LjcxMjQsIDIzLjA3OTksIDQwKSkKdGFpbChTYW1wbGVzKQpgYGAKCllvdSBjYW4gYWxzbyB1c2UgYW5vdGhlciBgZGF0YS5mcmFtZWAsIHdoaWNoIGluIHRoaXMgY2FzZSBtdXN0IGhhdmUgdGhlIHNhbWUgbmFtZWQgdmFyYWlibGVzIGFzIHRoZSBvcmlnaW5hbC4gIAoKYGBge3J9Cm1vcmVzaXRlcyA8LSBkYXRhLmZyYW1lKCBJRCA9IDQxOjQyLAogICAgICAgICAgICAgICAgICAgICAgICAgU3RyYXR1bSA9IGMoIkxvcyBCYXJyaWxlcyIsIkNvbW9uZHUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIExvbmdpdHVkZSA9IGMoLTEwOS43MDI2LCAtMTExLjg0NDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgTGF0aXR1ZGUgPSBjKDIzLjY4MTEsIDI2LjA3MDgpIAogICAgICAgICAgICAgICAgICAgICAgICAgKQpuYW1lcyhtb3Jlc2l0ZXMpCmBgYAoKSG93ZXZlciwgbm90aWNlIHRoYXQgdGhlIG9yZGVyIGluIHdoaWNoIHRoZXNlIHZhcmFpYmxlcyBpbiBgbW9yZXNpdGVzYCBpcyBkaWZmZXJlbnQgdGhhbiB0aG9zZSBpbiBgU2FtcGxlc2AuICBUaGUgYHJiaW5kKClgIGZ1bmN0aW9uIHJlYXJyYW5nZXMgdGhlIG9yZGVyIHdoZW4gaXQgY29uY2F0ZW5hdGVzIG9udG8gdGhlIHJvd3MuCgpgYGB7cn0KU2FtcGxlcyA8LSByYmluZCggU2FtcGxlcywgbW9yZXNpdGVzKQp0YWlsKCBTYW1wbGVzICkKYGBgCgpUbyBkZWxldGUgYSBjb2x1bW4gb2YgZGF0YSwgYXNzaWduIGl0IHRoZSB2YWx1ZSBvZiBgTlVMTGAuCgpgYGB7cn0KU2FtcGxlcyRJRCA8LSBOVUxMCnN1bW1hcnkoU2FtcGxlcykKYGBgCgpvciB1c2UgbmVnYXRpdmUgaW5kaWNlcyB0byBkZWxldGUgcm93cy4KCmBgYHtyfQpTYW1wbGVzIDwtIFNhbXBsZXNbIC00MjotNDAsIF0Kc3VtbWFyeShTYW1wbGVzKQpgYGAKCi0tLQoKVGhlIGBkYXRhLmZyYW1lYCBvYmplY3QgaXMgYSBmdW5kYW1lbnRhbCBjb21wb25lbnQgb2YgeW91ciBgUmAgd29yayBmbG93IGFuZCBiZWluZyBhYmxlIHRvIG1hbmlwdWxhdGUgZGF0YSB3aXRoaW4gaXQgYW5kIGV4dHJhY3QgZGF0YSBmcm9tIGl0IGFyZSBhIGh1Z2UgcGFydCBvZiBiZWNvbWluZyBkYXRhIGxpdGVyYXRlLgoKCgoKCgoKCgoKCgoKCgoKCgoK