1 What is plyr?

Plyr is a R package that makes it easy to split apart, manipulate, and put back together data.

1.1 Why is it better than split and apply?

Plyr is easier because it uses the common syntax and faster because it uses an input-output format requiring less code. Additionally there are few apply functions that allow dataframes to be the input and output, whereas plyr is mainly used to manipulate dataframes. Plyr can also summarize dataframes into new dataframes, which can be useful when extracting values from large datasets. If you are not familiar with the split and apply method please refer to Chapter 4.

2 Plyr basics

2.1 Plyr setup and install

To start using plyr install and load the plyr library.
All examples in this chapter will be based on the Weather dataframe below. This dataset consists of 5 columns and 364 rows; the first three columns are the date and month followed by the season that particular day is in. The last two columns are the average daily temperature and percipitation for each date. If you are not familiar with generating dataframes please refer to Chapter 1

## intall.packages(plyr)
  
library(plyr) 

#Building the Weather dataframe
NJanuary = 31 
NFebruary = 28
NMarch=31
NApril=30
NMay=31
NJune=30
NJuly=31
NAugust=30
NSeptember=31
NOctober=30
NNovember=31
NDecember=30

Day<-c(seq(1:NJanuary),seq(1:NFebruary),seq(1:NMarch),seq(1:NApril),seq(1:NMay),seq(1:NJune),seq(1:NJuly),seq(1:NAugust),seq(1:NSeptember), seq(1:NOctober),seq(1:NNovember),seq(1:NDecember))

Month<-c(rep("January",NJanuary),rep("February",NFebruary),rep("March",NMarch),rep("April",NApril),rep("May",NMay),rep("June",NJune),rep("July",NJuly),rep("August",NAugust),rep("September",NSeptember),rep("October",NOctober),rep("November",NNovember),rep("December",NDecember))

Season<-c(rep("Winter",sum(NJanuary+NFebruary+20)),rep("Spring",sum(NMarch+NApril+NMay)),rep("Summer",sum(NJune+NJuly+NAugust+2)),rep("Fall",sum(NSeptember+NOctober+NNovember-1)),rep("Winter",sum(NDecember-21)))

set.seed(4)
Temperature<-c(rnorm(NJanuary,mean=21,sd=10.2),c(rnorm(NFebruary,mean=25,sd=8.6)),c(rnorm(NMarch,mean=37,sd=12.2)),c(rnorm(NApril,mean=50,sd=10.1)),c(rnorm(NMay,mean=59,sd=7)),c(rnorm(NJune,mean=70,sd=8.6)),c(rnorm(NJuly,mean=73,sd=11.4)),c(rnorm(NAugust,mean=72,sd=12.3)),c(rnorm(NSeptember,mean=64,sd=11.6)),c(rnorm(NOctober,mean=54,sd=7.8)),c(rnorm(NNovember,mean=42,sd=13.4)),c(rnorm(NDecember,mean=27,sd=8.6)))

set.seed(4)
Percipitation<-c(rnorm(NJanuary,mean=1.73,sd=.3),c(rnorm(NFebruary,mean=1.79,sd=.3)),c(rnorm(NMarch,mean=2.5,sd=1.4)),c(rnorm(NApril,mean=3.38,sd=.5)),c(rnorm(NMay,mean=3.68,sd=.9)),c(rnorm(NJune,mean=3.45,sd=1.2)),c(rnorm(NJuly,mean=3.7,sd=.8)),c(rnorm(NAugust,mean=4.9,sd=2.7)),c(rnorm(NSeptember,mean=3.21,sd=1.4)),c(rnorm(NOctober,mean=3.15,sd=.9)),c(rnorm(NNovember,mean=3.15,sd=1.3)),c(rnorm(NDecember,mean=2.24,sd=1.1)))

Weather<-data.frame(
  Day = Day,
  Month = Month,
  Season = Season,
  Temperature = Temperature,
  Percipitation = Percipitation
  )

head(Weather)
##   Day   Month Season Temperature Percipitation
## 1   1 January Winter    23.21090      1.795026
## 2   2 January Winter    15.46658      1.567252
## 3   3 January Winter    30.08968      1.997343
## 4   4 January Winter    27.07900      1.908794
## 5   5 January Winter    37.68330      2.220685
## 6   6 January Winter    28.03061      1.936783

2.2 Basic syntax

Plyr uses the base ply() which is preceded by two letters that indicate the format of the data. The first letter denotes the format in and the second letter specifies the format out. Plyr requires the specifications of data, variable, and function after the ply base. Below are the most common data formats for plyr:

  • d = dataframe
  • a = array/matrix
  • l = list

ddply is the most commonly used format. As the first 2 letters indicate, ddply takes an existing dataframe, splits it apart, extracts some information from it, and either makes a new dataframe or alters the original dataframe to include the new information.

2.3 Example

ddply on its own can be used to sort the dataframe by a specific column without losing the corresponding values in other columns. The example below takes the Weather dataframe and generates a new dataframe, Temperature.dd, that is sorted by increasing temperature. This is done using the default function which is null. The original Weather dataframe could also have been resorted by recreating the object Weather instead of Temperature.dd

Temperature.dd<-ddply(Weather, .(Temperature))

head(Temperature.dd)
##   Day    Month Season Temperature Percipitation
## 1   7  January Winter    7.931284     1.3456260
## 2  19 February Winter    9.542515     1.2507854
## 3  13 February Winter   10.482782     1.2835854
## 4  11 November   Fall   11.523199     0.1932954
## 5  29  January Winter   11.534113     1.4515916
## 6   9 December   Fall   11.986117     0.3196197

3 Common functions

The 3 plyr functions that are most commonly used are transform, summarise, and mutate. These functions are called for within the plyr command.

3.1 Transform

Transform modifies an existing dataframe. This can be useful if you want to compute values for an additional column in the dataframe based on information already available.

3.1.1 Transform example

In this example we will add the additional variable of average monthly temperature to the Weather dataframe. To do this, trasform will be called for in place of the function and then the new column name and the calculation of the new variable will be calculated. Note that the new column will be called mean.month.

Weather.1<-ddply(Weather,.(Month), transform,
               mean.month=mean(Temperature))

head(Weather.1)
##   Day Month Season Temperature Percipitation mean.month
## 1   1 April Spring    37.09563      2.741170   50.66127
## 2   2 April Spring    41.94007      2.980994   50.66127
## 3   3 April Spring    51.60673      3.459541   50.66127
## 4   4 April Spring    56.20946      3.687399   50.66127
## 5   5 April Spring    56.94827      3.723974   50.66127
## 6   6 April Spring    49.52478      3.356474   50.66127

3.2 Summarise

Summarise creates a new condensed dataframe that can summarise information about the existing dataframe. This is a similar tool to pivot tables in Excel except it allows you to summarise a wider range of variables. Please note that summarise is spelt with an ā€œsā€.

3.2.1 Summarise example 1

In this example we will summarise the average monthly temperature, the average monthly percipitation, and the standard deviation of both into new columns. The format of this is very similar to transform in that summarise is called for in place of the plyr function and then the names of the new columns as well as the formulas needed to generate the new columns are specified after.

Weather.summary<-ddply(Weather, .(Month), summarise,
                       avg.temp=mean(Temperature),
                       sd.temp=sd(Temperature),
                       avg.perc=mean(Percipitation),
                       sd.perc=sd(Percipitation)
                       )

head(Weather.summary)
##      Month avg.temp   sd.temp avg.perc   sd.perc
## 1    April 50.66127  9.717198 3.412736 0.4810494
## 2   August 71.65274 10.372217 4.823772 2.2768282
## 3 December 27.09597  6.941381 2.252275 0.8878510
## 4 February 24.20208  8.082064 1.762166 0.2819325
## 5  January 25.85950  8.122852 1.872927 0.2389074
## 6     July 72.94086 11.973996 3.695850 0.8402804

3.2.2 Summarise example 2

The next example will summarise the average temperature and percipitation per season. The code for this is very similar to the first summarise example except we will change the variable that items are summarised by to Season

In this example we will summarise the average monthly temperature, the average monthly percipitation, and the standard deviation of both into new columns. The format of this is very similar to transform in that summarise is called for in place of the plyr function and then the names of the new columns as well as the formulas needed to generate the new columns are specified after.

Weather.summary2<-ddply(Weather, .(Season), summarise,
                       avg.temp=mean(Temperature),
                       avg.perc=mean(Percipitation)
                       )

head(Weather.summary2)
##   Season avg.temp avg.perc
## 1   Fall 44.35078 2.912997
## 2 Spring 55.08098 3.285695
## 3 Summer 69.66207 3.858623
## 4 Winter 28.25241 2.050919

3.3 Mutate

Mutate is a very powerful tool that is similar to transform and summarise, but it allows variables computed within the command to be used immediately to compute other variables, all of which can be incorporated back into the original data frame.

3.3.1 Mutate example

This example creates columns that calculate the average monthly temperature, percipitation and the standard deviation of these variables. The newly calculated standard deviations are then used to generate columns calculating the standard errors.

Weather<-ddply(Weather, .(Month), mutate,
                      avg.temp=mean(Temperature),
                      sd.temp=sd(Temperature),
                      se.temp=sd.temp/sqrt(length(Month)),
                      avg.perc=mean(Percipitation),
                      sd.perc=sd(Percipitation),
                      se.perc=sd.perc/sqrt(length(Month))
                       )

head(Weather)
##   Day Month Season Temperature Percipitation avg.temp  sd.temp se.temp
## 1   1 April Spring    37.09563      2.741170 50.66127 9.717198 1.77411
## 2   2 April Spring    41.94007      2.980994 50.66127 9.717198 1.77411
## 3   3 April Spring    51.60673      3.459541 50.66127 9.717198 1.77411
## 4   4 April Spring    56.20946      3.687399 50.66127 9.717198 1.77411
## 5   5 April Spring    56.94827      3.723974 50.66127 9.717198 1.77411
## 6   6 April Spring    49.52478      3.356474 50.66127 9.717198 1.77411
##   avg.perc   sd.perc    se.perc
## 1 3.412736 0.4810494 0.08782721
## 2 3.412736 0.4810494 0.08782721
## 3 3.412736 0.4810494 0.08782721
## 4 3.412736 0.4810494 0.08782721
## 5 3.412736 0.4810494 0.08782721
## 6 3.412736 0.4810494 0.08782721

4 References

Sean Anderson
Cookbook for R

LS0tDQp0aXRsZTogIkNoYXB0ZXIgNTogcGx5ciINCmF1dGhvcjogIlNhbWkgQ29yd2luIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBmb250c2l6ZTogOHB0DQogICAgdG9jOiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCg0KYGBgDQoNCg0KIyBXaGF0IGlzIHBseXI/DQo+IFBseXIgaXMgYSBSIHBhY2thZ2UgdGhhdCBtYWtlcyBpdCBlYXN5IHRvIHNwbGl0IGFwYXJ0LCBtYW5pcHVsYXRlLCBhbmQgcHV0IGJhY2sgdG9nZXRoZXIgZGF0YS4gDQoNCiMjIFdoeSBpcyBpdCBiZXR0ZXIgdGhhbiBzcGxpdCBhbmQgYXBwbHk/DQpQbHlyIGlzICoqZWFzaWVyKiogYmVjYXVzZSBpdCB1c2VzIHRoZSBjb21tb24gc3ludGF4IGFuZCA8Yj5mYXN0ZXI8L2I+IGJlY2F1c2UgaXQgdXNlcyBhbiBpbnB1dC1vdXRwdXQgZm9ybWF0IHJlcXVpcmluZyBsZXNzIGNvZGUuICBBZGRpdGlvbmFsbHkgdGhlcmUgYXJlIGZldyBhcHBseSBmdW5jdGlvbnMgdGhhdCBhbGxvdyBkYXRhZnJhbWVzIHRvIGJlIHRoZSBpbnB1dCBhbmQgb3V0cHV0LCB3aGVyZWFzIHBseXIgaXMgbWFpbmx5IHVzZWQgdG8gbWFuaXB1bGF0ZSBkYXRhZnJhbWVzLiBQbHlyIGNhbiBhbHNvIHN1bW1hcml6ZSBkYXRhZnJhbWVzIGludG8gbmV3IGRhdGFmcmFtZXMsIHdoaWNoIGNhbiBiZSB1c2VmdWwgd2hlbiBleHRyYWN0aW5nIHZhbHVlcyBmcm9tIGxhcmdlIGRhdGFzZXRzLiBJZiB5b3UgYXJlIG5vdCBmYW1pbGlhciB3aXRoIHRoZSBzcGxpdCBhbmQgYXBwbHkgbWV0aG9kIHBsZWFzZSByZWZlciB0byBbQ2hhcHRlciA0XShodHRwOi8vYWRlbW9zLnBlb3BsZS51aWMuZWR1L0NoYXB0ZXI0Lmh0bWwpLg0KDQoNCiMgUGx5ciBiYXNpY3MNCiMjIFBseXIgc2V0dXAgYW5kIGluc3RhbGwNClRvIHN0YXJ0IHVzaW5nIHBseXIgaW5zdGFsbCBhbmQgbG9hZCB0aGUgcGx5ciBsaWJyYXJ5LiAgDQpBbGwgZXhhbXBsZXMgaW4gdGhpcyBjaGFwdGVyIHdpbGwgYmUgYmFzZWQgb24gdGhlIFdlYXRoZXIgZGF0YWZyYW1lIGJlbG93LiBUaGlzIGRhdGFzZXQgY29uc2lzdHMgb2YgNSBjb2x1bW5zIGFuZCAzNjQgcm93czsgdGhlIGZpcnN0IHRocmVlIGNvbHVtbnMgYXJlIHRoZSBkYXRlIGFuZCBtb250aCBmb2xsb3dlZCBieSB0aGUgc2Vhc29uIHRoYXQgcGFydGljdWxhciBkYXkgaXMgaW4uICBUaGUgbGFzdCB0d28gY29sdW1ucyBhcmUgdGhlIGF2ZXJhZ2UgZGFpbHkgdGVtcGVyYXR1cmUgYW5kIHBlcmNpcGl0YXRpb24gZm9yIGVhY2ggZGF0ZS4gIElmIHlvdSBhcmUgbm90IGZhbWlsaWFyIHdpdGggZ2VuZXJhdGluZyBkYXRhZnJhbWVzIHBsZWFzZSByZWZlciB0byBbQ2hhcHRlciAxXShodHRwOi8vYWRlbW9zLnBlb3BsZS51aWMuZWR1L0NoYXB0ZXIxLmh0bWwjOV9kYXRhZnJhbWVzKQ0KDQpgYGAge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NCiMjIGludGFsbC5wYWNrYWdlcyhwbHlyKQ0KICANCmxpYnJhcnkocGx5cikgDQoNCiNCdWlsZGluZyB0aGUgV2VhdGhlciBkYXRhZnJhbWUNCk5KYW51YXJ5ID0gMzEgDQpORmVicnVhcnkgPSAyOA0KTk1hcmNoPTMxDQpOQXByaWw9MzANCk5NYXk9MzENCk5KdW5lPTMwDQpOSnVseT0zMQ0KTkF1Z3VzdD0zMA0KTlNlcHRlbWJlcj0zMQ0KTk9jdG9iZXI9MzANCk5Ob3ZlbWJlcj0zMQ0KTkRlY2VtYmVyPTMwDQoNCkRheTwtYyhzZXEoMTpOSmFudWFyeSksc2VxKDE6TkZlYnJ1YXJ5KSxzZXEoMTpOTWFyY2gpLHNlcSgxOk5BcHJpbCksc2VxKDE6Tk1heSksc2VxKDE6Tkp1bmUpLHNlcSgxOk5KdWx5KSxzZXEoMTpOQXVndXN0KSxzZXEoMTpOU2VwdGVtYmVyKSwgc2VxKDE6Tk9jdG9iZXIpLHNlcSgxOk5Ob3ZlbWJlciksc2VxKDE6TkRlY2VtYmVyKSkNCg0KTW9udGg8LWMocmVwKCJKYW51YXJ5IixOSmFudWFyeSkscmVwKCJGZWJydWFyeSIsTkZlYnJ1YXJ5KSxyZXAoIk1hcmNoIixOTWFyY2gpLHJlcCgiQXByaWwiLE5BcHJpbCkscmVwKCJNYXkiLE5NYXkpLHJlcCgiSnVuZSIsTkp1bmUpLHJlcCgiSnVseSIsTkp1bHkpLHJlcCgiQXVndXN0IixOQXVndXN0KSxyZXAoIlNlcHRlbWJlciIsTlNlcHRlbWJlcikscmVwKCJPY3RvYmVyIixOT2N0b2JlcikscmVwKCJOb3ZlbWJlciIsTk5vdmVtYmVyKSxyZXAoIkRlY2VtYmVyIixORGVjZW1iZXIpKQ0KDQpTZWFzb248LWMocmVwKCJXaW50ZXIiLHN1bShOSmFudWFyeStORmVicnVhcnkrMjApKSxyZXAoIlNwcmluZyIsc3VtKE5NYXJjaCtOQXByaWwrTk1heSkpLHJlcCgiU3VtbWVyIixzdW0oTkp1bmUrTkp1bHkrTkF1Z3VzdCsyKSkscmVwKCJGYWxsIixzdW0oTlNlcHRlbWJlcitOT2N0b2JlcitOTm92ZW1iZXItMSkpLHJlcCgiV2ludGVyIixzdW0oTkRlY2VtYmVyLTIxKSkpDQoNCnNldC5zZWVkKDQpDQpUZW1wZXJhdHVyZTwtYyhybm9ybShOSmFudWFyeSxtZWFuPTIxLHNkPTEwLjIpLGMocm5vcm0oTkZlYnJ1YXJ5LG1lYW49MjUsc2Q9OC42KSksYyhybm9ybShOTWFyY2gsbWVhbj0zNyxzZD0xMi4yKSksYyhybm9ybShOQXByaWwsbWVhbj01MCxzZD0xMC4xKSksYyhybm9ybShOTWF5LG1lYW49NTksc2Q9NykpLGMocm5vcm0oTkp1bmUsbWVhbj03MCxzZD04LjYpKSxjKHJub3JtKE5KdWx5LG1lYW49NzMsc2Q9MTEuNCkpLGMocm5vcm0oTkF1Z3VzdCxtZWFuPTcyLHNkPTEyLjMpKSxjKHJub3JtKE5TZXB0ZW1iZXIsbWVhbj02NCxzZD0xMS42KSksYyhybm9ybShOT2N0b2JlcixtZWFuPTU0LHNkPTcuOCkpLGMocm5vcm0oTk5vdmVtYmVyLG1lYW49NDIsc2Q9MTMuNCkpLGMocm5vcm0oTkRlY2VtYmVyLG1lYW49Mjcsc2Q9OC42KSkpDQoNCnNldC5zZWVkKDQpDQpQZXJjaXBpdGF0aW9uPC1jKHJub3JtKE5KYW51YXJ5LG1lYW49MS43MyxzZD0uMyksYyhybm9ybShORmVicnVhcnksbWVhbj0xLjc5LHNkPS4zKSksYyhybm9ybShOTWFyY2gsbWVhbj0yLjUsc2Q9MS40KSksYyhybm9ybShOQXByaWwsbWVhbj0zLjM4LHNkPS41KSksYyhybm9ybShOTWF5LG1lYW49My42OCxzZD0uOSkpLGMocm5vcm0oTkp1bmUsbWVhbj0zLjQ1LHNkPTEuMikpLGMocm5vcm0oTkp1bHksbWVhbj0zLjcsc2Q9LjgpKSxjKHJub3JtKE5BdWd1c3QsbWVhbj00Ljksc2Q9Mi43KSksYyhybm9ybShOU2VwdGVtYmVyLG1lYW49My4yMSxzZD0xLjQpKSxjKHJub3JtKE5PY3RvYmVyLG1lYW49My4xNSxzZD0uOSkpLGMocm5vcm0oTk5vdmVtYmVyLG1lYW49My4xNSxzZD0xLjMpKSxjKHJub3JtKE5EZWNlbWJlcixtZWFuPTIuMjQsc2Q9MS4xKSkpDQoNCldlYXRoZXI8LWRhdGEuZnJhbWUoDQogIERheSA9IERheSwNCiAgTW9udGggPSBNb250aCwNCiAgU2Vhc29uID0gU2Vhc29uLA0KICBUZW1wZXJhdHVyZSA9IFRlbXBlcmF0dXJlLA0KICBQZXJjaXBpdGF0aW9uID0gUGVyY2lwaXRhdGlvbg0KICApDQoNCmhlYWQoV2VhdGhlcikNCg0KDQpgYGANCiMjIEJhc2ljIHN5bnRheA0KUGx5ciB1c2VzIHRoZSBiYXNlIHBseSgpIHdoaWNoIGlzIHByZWNlZGVkIGJ5IHR3byBsZXR0ZXJzIHRoYXQgaW5kaWNhdGUgdGhlIGZvcm1hdCBvZiB0aGUgZGF0YS4gVGhlIGZpcnN0IGxldHRlciBkZW5vdGVzIHRoZSBmb3JtYXQgaW4gYW5kIHRoZSBzZWNvbmQgbGV0dGVyIHNwZWNpZmllcyB0aGUgZm9ybWF0IG91dC4gUGx5ciByZXF1aXJlcyB0aGUgc3BlY2lmaWNhdGlvbnMgb2YgZGF0YSwgdmFyaWFibGUsIGFuZCBmdW5jdGlvbiBhZnRlciB0aGUgcGx5IGJhc2UuICAgQmVsb3cgYXJlIHRoZSBtb3N0IGNvbW1vbiBkYXRhIGZvcm1hdHMgZm9yIHBseXI6DQoNCi0gZCA9IGRhdGFmcmFtZQ0KLSBhID0gYXJyYXkvbWF0cml4DQotIGwgPSBsaXN0DQoNCmRkcGx5IGlzIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgZm9ybWF0LiBBcyB0aGUgZmlyc3QgMiBsZXR0ZXJzIGluZGljYXRlLCBkZHBseSB0YWtlcyBhbiBleGlzdGluZyBkYXRhZnJhbWUsIHNwbGl0cyBpdCBhcGFydCwgZXh0cmFjdHMgc29tZSBpbmZvcm1hdGlvbiBmcm9tIGl0LCBhbmQgZWl0aGVyIG1ha2VzIGEgbmV3IGRhdGFmcmFtZSBvciBhbHRlcnMgdGhlIG9yaWdpbmFsIGRhdGFmcmFtZSB0byBpbmNsdWRlIHRoZSBuZXcgaW5mb3JtYXRpb24uIA0KDQojIyBFeGFtcGxlDQpkZHBseSBvbiBpdHMgb3duIGNhbiBiZSB1c2VkIHRvIHNvcnQgdGhlIGRhdGFmcmFtZSBieSBhIHNwZWNpZmljIGNvbHVtbiB3aXRob3V0IGxvc2luZyB0aGUgY29ycmVzcG9uZGluZyB2YWx1ZXMgaW4gb3RoZXIgY29sdW1ucy4gVGhlIGV4YW1wbGUgYmVsb3cgdGFrZXMgdGhlIFdlYXRoZXIgZGF0YWZyYW1lIGFuZCBnZW5lcmF0ZXMgYSBuZXcgZGF0YWZyYW1lLCBUZW1wZXJhdHVyZS5kZCwgdGhhdCBpcyBzb3J0ZWQgYnkgaW5jcmVhc2luZyB0ZW1wZXJhdHVyZS4gVGhpcyBpcyBkb25lIHVzaW5nIHRoZSBkZWZhdWx0IGZ1bmN0aW9uIHdoaWNoIGlzIG51bGwuICBUaGUgb3JpZ2luYWwgV2VhdGhlciBkYXRhZnJhbWUgY291bGQgYWxzbyBoYXZlIGJlZW4gcmVzb3J0ZWQgYnkgcmVjcmVhdGluZyB0aGUgb2JqZWN0IFdlYXRoZXIgaW5zdGVhZCBvZiBUZW1wZXJhdHVyZS5kZA0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpUZW1wZXJhdHVyZS5kZDwtZGRwbHkoV2VhdGhlciwgLihUZW1wZXJhdHVyZSkpDQoNCmhlYWQoVGVtcGVyYXR1cmUuZGQpDQpgYGANCg0KIyBDb21tb24gZnVuY3Rpb25zDQpUaGUgMyBwbHlyIGZ1bmN0aW9ucyB0aGF0IGFyZSBtb3N0IGNvbW1vbmx5IHVzZWQgYXJlIHRyYW5zZm9ybSwgc3VtbWFyaXNlLCBhbmQgbXV0YXRlLiAgVGhlc2UgZnVuY3Rpb25zIGFyZSBjYWxsZWQgZm9yIHdpdGhpbiB0aGUgcGx5ciBjb21tYW5kLiANCg0KIyMgVHJhbnNmb3JtDQpUcmFuc2Zvcm0gbW9kaWZpZXMgYW4gZXhpc3RpbmcgZGF0YWZyYW1lLiAgVGhpcyBjYW4gYmUgdXNlZnVsIGlmIHlvdSB3YW50IHRvIGNvbXB1dGUgdmFsdWVzIGZvciBhbiBhZGRpdGlvbmFsIGNvbHVtbiBpbiB0aGUgZGF0YWZyYW1lIGJhc2VkIG9uIGluZm9ybWF0aW9uIGFscmVhZHkgYXZhaWxhYmxlLg0KDQojIyMgVHJhbnNmb3JtIGV4YW1wbGUNCkluIHRoaXMgZXhhbXBsZSB3ZSB3aWxsIGFkZCB0aGUgYWRkaXRpb25hbCB2YXJpYWJsZSBvZiBhdmVyYWdlIG1vbnRobHkgdGVtcGVyYXR1cmUgdG8gdGhlIFdlYXRoZXIgZGF0YWZyYW1lLiBUbyBkbyB0aGlzLCB0cmFzZm9ybSB3aWxsIGJlIGNhbGxlZCBmb3IgaW4gcGxhY2Ugb2YgdGhlIGZ1bmN0aW9uIGFuZCB0aGVuIHRoZSBuZXcgY29sdW1uIG5hbWUgYW5kIHRoZSBjYWxjdWxhdGlvbiBvZiB0aGUgbmV3IHZhcmlhYmxlIHdpbGwgYmUgY2FsY3VsYXRlZC4gIE5vdGUgdGhhdCB0aGUgbmV3IGNvbHVtbiB3aWxsIGJlIGNhbGxlZCBtZWFuLm1vbnRoLg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpXZWF0aGVyLjE8LWRkcGx5KFdlYXRoZXIsLihNb250aCksIHRyYW5zZm9ybSwNCiAgICAgICAgICAgICAgIG1lYW4ubW9udGg9bWVhbihUZW1wZXJhdHVyZSkpDQoNCmhlYWQoV2VhdGhlci4xKQ0KYGBgDQoNCiMjIFN1bW1hcmlzZQ0KU3VtbWFyaXNlIGNyZWF0ZXMgYSBuZXcgY29uZGVuc2VkIGRhdGFmcmFtZSB0aGF0IGNhbiBzdW1tYXJpc2UgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGV4aXN0aW5nIGRhdGFmcmFtZS4gIFRoaXMgaXMgYSBzaW1pbGFyIHRvb2wgdG8gcGl2b3QgdGFibGVzIGluIEV4Y2VsIGV4Y2VwdCBpdCBhbGxvd3MgeW91IHRvIHN1bW1hcmlzZSBhIHdpZGVyIHJhbmdlIG9mIHZhcmlhYmxlcy4gUGxlYXNlIG5vdGUgdGhhdCBzdW1tYXJpc2UgaXMgc3BlbHQgd2l0aCBhbiAicyIuICANCg0KIyMjIFN1bW1hcmlzZSBleGFtcGxlIDENCkluIHRoaXMgZXhhbXBsZSB3ZSB3aWxsIHN1bW1hcmlzZSB0aGUgYXZlcmFnZSBtb250aGx5IHRlbXBlcmF0dXJlLCB0aGUgYXZlcmFnZSBtb250aGx5IHBlcmNpcGl0YXRpb24sIGFuZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGJvdGggaW50byBuZXcgY29sdW1ucy4gVGhlIGZvcm1hdCBvZiB0aGlzIGlzIHZlcnkgc2ltaWxhciB0byB0cmFuc2Zvcm0gaW4gdGhhdCAgc3VtbWFyaXNlIGlzIGNhbGxlZCBmb3IgaW4gcGxhY2Ugb2YgdGhlIHBseXIgZnVuY3Rpb24gYW5kIHRoZW4gdGhlIG5hbWVzIG9mIHRoZSBuZXcgY29sdW1ucyBhcyB3ZWxsIGFzIHRoZSBmb3JtdWxhcyBuZWVkZWQgdG8gZ2VuZXJhdGUgdGhlIG5ldyBjb2x1bW5zIGFyZSBzcGVjaWZpZWQgYWZ0ZXIuICANCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KV2VhdGhlci5zdW1tYXJ5PC1kZHBseShXZWF0aGVyLCAuKE1vbnRoKSwgc3VtbWFyaXNlLA0KICAgICAgICAgICAgICAgICAgICAgICBhdmcudGVtcD1tZWFuKFRlbXBlcmF0dXJlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgc2QudGVtcD1zZChUZW1wZXJhdHVyZSksDQogICAgICAgICAgICAgICAgICAgICAgIGF2Zy5wZXJjPW1lYW4oUGVyY2lwaXRhdGlvbiksDQogICAgICAgICAgICAgICAgICAgICAgIHNkLnBlcmM9c2QoUGVyY2lwaXRhdGlvbikNCiAgICAgICAgICAgICAgICAgICAgICAgKQ0KDQpoZWFkKFdlYXRoZXIuc3VtbWFyeSkNCg0KDQpgYGANCg0KIyMjIFN1bW1hcmlzZSBleGFtcGxlIDINClRoZSBuZXh0IGV4YW1wbGUgd2lsbCBzdW1tYXJpc2UgdGhlIGF2ZXJhZ2UgdGVtcGVyYXR1cmUgYW5kIHBlcmNpcGl0YXRpb24gcGVyIHNlYXNvbi4gIFRoZSBjb2RlIGZvciB0aGlzIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgZmlyc3Qgc3VtbWFyaXNlIGV4YW1wbGUgZXhjZXB0IHdlIHdpbGwgY2hhbmdlIHRoZSB2YXJpYWJsZSB0aGF0IGl0ZW1zIGFyZSBzdW1tYXJpc2VkIGJ5IHRvIFNlYXNvbg0KDQpJbiB0aGlzIGV4YW1wbGUgd2Ugd2lsbCBzdW1tYXJpc2UgdGhlIGF2ZXJhZ2UgbW9udGhseSB0ZW1wZXJhdHVyZSwgdGhlIGF2ZXJhZ2UgbW9udGhseSBwZXJjaXBpdGF0aW9uLCBhbmQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBib3RoIGludG8gbmV3IGNvbHVtbnMuIFRoZSBmb3JtYXQgb2YgdGhpcyBpcyB2ZXJ5IHNpbWlsYXIgdG8gdHJhbnNmb3JtIGluIHRoYXQgIHN1bW1hcmlzZSBpcyBjYWxsZWQgZm9yIGluIHBsYWNlIG9mIHRoZSBwbHlyIGZ1bmN0aW9uIGFuZCB0aGVuIHRoZSBuYW1lcyBvZiB0aGUgbmV3IGNvbHVtbnMgYXMgd2VsbCBhcyB0aGUgZm9ybXVsYXMgbmVlZGVkIHRvIGdlbmVyYXRlIHRoZSBuZXcgY29sdW1ucyBhcmUgc3BlY2lmaWVkIGFmdGVyLiAgDQpgYGAge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NCg0KV2VhdGhlci5zdW1tYXJ5MjwtZGRwbHkoV2VhdGhlciwgLihTZWFzb24pLCBzdW1tYXJpc2UsDQogICAgICAgICAgICAgICAgICAgICAgIGF2Zy50ZW1wPW1lYW4oVGVtcGVyYXR1cmUpLA0KICAgICAgICAgICAgICAgICAgICAgICBhdmcucGVyYz1tZWFuKFBlcmNpcGl0YXRpb24pDQogICAgICAgICAgICAgICAgICAgICAgICkNCg0KaGVhZChXZWF0aGVyLnN1bW1hcnkyKQ0KYGBgDQoNCiMjTXV0YXRlDQpNdXRhdGUgaXMgYSB2ZXJ5IHBvd2VyZnVsIHRvb2wgdGhhdCBpcyBzaW1pbGFyIHRvIHRyYW5zZm9ybSBhbmQgc3VtbWFyaXNlLCBidXQgaXQgYWxsb3dzIHZhcmlhYmxlcyBjb21wdXRlZCB3aXRoaW4gdGhlIGNvbW1hbmQgdG8gYmUgdXNlZCBpbW1lZGlhdGVseSB0byBjb21wdXRlIG90aGVyIHZhcmlhYmxlcywgYWxsIG9mIHdoaWNoIGNhbiBiZSBpbmNvcnBvcmF0ZWQgYmFjayBpbnRvIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lLg0KDQojIyMgTXV0YXRlIGV4YW1wbGUNClRoaXMgZXhhbXBsZSBjcmVhdGVzIGNvbHVtbnMgdGhhdCBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgbW9udGhseSB0ZW1wZXJhdHVyZSwgcGVyY2lwaXRhdGlvbiBhbmQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGVzZSB2YXJpYWJsZXMuIFRoZSBuZXdseSBjYWxjdWxhdGVkIHN0YW5kYXJkIGRldmlhdGlvbnMgYXJlIHRoZW4gdXNlZCB0byBnZW5lcmF0ZSBjb2x1bW5zIGNhbGN1bGF0aW5nIHRoZSBzdGFuZGFyZCBlcnJvcnMuIA0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpXZWF0aGVyPC1kZHBseShXZWF0aGVyLCAuKE1vbnRoKSwgbXV0YXRlLA0KICAgICAgICAgICAgICAgICAgICAgIGF2Zy50ZW1wPW1lYW4oVGVtcGVyYXR1cmUpLA0KICAgICAgICAgICAgICAgICAgICAgIHNkLnRlbXA9c2QoVGVtcGVyYXR1cmUpLA0KICAgICAgICAgICAgICAgICAgICAgIHNlLnRlbXA9c2QudGVtcC9zcXJ0KGxlbmd0aChNb250aCkpLA0KICAgICAgICAgICAgICAgICAgICAgIGF2Zy5wZXJjPW1lYW4oUGVyY2lwaXRhdGlvbiksDQogICAgICAgICAgICAgICAgICAgICAgc2QucGVyYz1zZChQZXJjaXBpdGF0aW9uKSwNCiAgICAgICAgICAgICAgICAgICAgICBzZS5wZXJjPXNkLnBlcmMvc3FydChsZW5ndGgoTW9udGgpKQ0KICAgICAgICAgICAgICAgICAgICAgICApDQoNCmhlYWQoV2VhdGhlcikNCg0KYGBgDQoNCiNSZWZlcmVuY2VzDQpbU2VhbiBBbmRlcnNvbl0oaHR0cDovL3NlYW5hbmRlcnNvbi5jYS8yMDEzLzEyLzAxL3BseXIuaHRtbCkgIA0KW0Nvb2tib29rIGZvciBSXShodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL01hbmlwdWxhdGluZ19kYXRhL1N1bW1hcml6aW5nX2RhdGEpDQoNCjxzY3JpcHQ+DQogIChmdW5jdGlvbihpLHMsbyxnLHIsYSxtKXtpWydHb29nbGVBbmFseXRpY3NPYmplY3QnXT1yO2lbcl09aVtyXXx8ZnVuY3Rpb24oKXsNCiAgKGlbcl0ucT1pW3JdLnF8fFtdKS5wdXNoKGFyZ3VtZW50cyl9LGlbcl0ubD0xKm5ldyBEYXRlKCk7YT1zLmNyZWF0ZUVsZW1lbnQobyksDQogIG09cy5nZXRFbGVtZW50c0J5VGFnTmFtZShvKVswXTthLmFzeW5jPTE7YS5zcmM9ZzttLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGEsbSkNCiAgfSkod2luZG93LGRvY3VtZW50LCdzY3JpcHQnLCdodHRwczovL3d3dy5nb29nbGUtYW5hbHl0aWNzLmNvbS9hbmFseXRpY3MuanMnLCdnYScpOw0KDQogIGdhKCdjcmVhdGUnLCAnVUEtOTg4Nzg3OTMtMScsICdhdXRvJyk7DQogIGdhKCdzZW5kJywgJ3BhZ2V2aWV3Jyk7DQoNCjwvc2NyaXB0Pg0KDQo=