Reading and Writing Files

This covers loading and saving data in an active workspace by reading and writing files.

8.1 R-Ready Data Sets

Enter data() at the R prompt to bring up a window listing these ready-to-use data sets along with a one-line description

data()

Use ???data(package = .packages(all.available = TRUE))??? to list the data sets in all available packages.

8.1.1 Built-in Data sets

To see a summary of the data sets contained in the package, you can use the library function library(help=“datasets”)

library(help="datasets")
?ChickWeight
ChickWeight[1:15,]
Grouped Data: weight ~ Time | Chick
   weight Time Chick Diet
1      42    0     1    1
2      51    2     1    1
3      59    4     1    1
4      64    6     1    1
5      76    8     1    1
6      93   10     1    1
7     106   12     1    1
8     125   14     1    1
9     149   16     1    1
10    171   18     1    1
11    199   20     1    1
12    205   21     1    1
13     40    0     2    1
14     49    2     2    1
15     58    4     2    1

8.1.2 Contributed Data Sets

Additional data sets are also available as contributed packages. To access them, first install and load the relevant package. Consider the data set ice.river which is in the contriubted package tseries by Trapletti and Hornik(2013)

First install the package by running the line install.packages(“tseries”) at the prompt. Then, to access the components of the pakcage, load it using library

install.packages("tseries"
                 )
library("tseries")
library(help="tseries")

You can enter ?ice.river to find more detals about th data set you want to work . The help file describes it as a “time series object” comprrised of river flow, precipitation, and temperature measurements. Data initially reported in Tong (1990)

?ice.river
Warning message:
In native_encode(res, to = encoding) :
  some characters may not work under the current locale

Icelandic River Data

Description

Contains the Icelandic river data as presented in Tong (1990), pages 432???440.

Usage

data(ice.river) Format

4 univariate time series flow.vat, flow.jok, prec, and temp, each with 1095 observations and the joint series ice.river.

Details

The series are daily observations from Jan. 1, 1972 to Dec. 31, 1974 on 4 variables: flow.vat, mean daily flow of Vatnsdalsa river (cms), flow.jok, mean daily flow of Jokulsa Eystri river (cms), prec, daily precipitation in Hveravellir (mm), and mean daily temperature in Hveravellir (deg C).

These datasets were introduced into the literature in a paper by Tong, Thanoon, and Gudmundsson (1985).

data(ice.river)
ice.river[1:5,]
     flow.vat flow.jok prec temp
[1,]     16.1     30.2  8.1  0.9
[2,]     19.2     29.0  4.4  1.6
[3,]     14.5     28.4  7.0  0.1
[4,]     11.0     27.8  0.0  0.6
[5,]     13.6     27.8  0.0  2.0

8.2 Reading in External Data Files

You often have to work with data from external sources. This shows how to do that.

8.2.1 The Table format

Table format files have 3 key features: Header (provides names for each column of data) Delimiter (character used to separate the entries in each line) Missing Value (character string used to exclusively denote a missing value)

Typically these files have a .txt extension or .csv Download the sample files from: https://www.nostarch.com/bookofr

mydatafile<- read.table(file="d:/bookofr/8.2.1_mydatafile.txt",
                        header = TRUE, 
                        sep=" ",
                        na.strings="*",
                        stringsAsFactors = FALSE)
mydatafile

Tip: if you are working with multiple files and don’t want to specify the working directory, you can set it with the setwd() command

# get working directory
getwd()
[1] "D:/Dropbox/BigData/R Programming/Work"
# change it back to original setting
setwd("D:/Dropbox/BigData/R Programming/Work")
getwd()
[1] "D:/Dropbox/BigData/R Programming/Work"
list.files("D:/Dropbox/BigData/R Programming/Work/bookofr")
 [1] "8.2.1_mydatafile.txt"       "8.2.2_spreadsheetfile.csv"  "Davies_Part1_Solutions.R"  
 [4] "Davies_Part1_Source_Code.R" "Davies_Part2_Solutions.R"   "Davies_Part2_Source_Code.R"
 [7] "Davies_Part3_Solutions.R"   "Davies_Part3_Source_Code.R" "Davies_Part4_Solutions.R"  
[10] "Davies_Part4_Source_Code.R" "Davies_Part5_Solutions.R"   "Davies_Part5_Source_Code.R"

You can also find files interactively using the file.choose command

file.choose()
[1] "D:\\Dropbox\\BigData\\R Programming\\Work\\bookofr\\8.2.1_mydatafile.txt"

So you can combine the output of the above command to load the file.

mydatafile<- read.table(file=file.choose(),
                        header = TRUE, 
                        sep=" ",
                        na.strings="*",
                        stringsAsFactors = FALSE)
mydatafile

Normally, read.table will convert non-numeric values into factors by default. To prevent this, we use the “stringsAsFactors=FALSE” To overide one of them as that data type:

# look at the current mydatafile before data type overide
mydatafile
# now we overide them
mydatafile$sex <- as.factor(mydatafile$sex)
mydatafile$funny <- factor(x=mydatafile$funny,levels=c("Low","Med","High"))
mydatafile

8.2.2 Spreadsheet Workbooks

Generally you will need to use file save as and save the xls or xlsx into .csv format. You can also use some contributed packages like gdata by Warnes et al. or XLConnect by Mirai Solutions

8.2.3 Web Based Files

You use the same read.table command, but this time in the file=“” area, use the URL

dia.url<-"http://www.amstat.org/publications/jse/v9n2/4cdata.txt"
diamonds <-read.table(dia.url)
diamonds
# now add the header using the names function
names(diamonds)<-c("Carat","Color","Clarity","Cert","Price")
# now list the table
diamonds[1:5,]

8.2.4 Other File Formats

Other format like .dat can be imported using read.table. Use the skip argument to remove the unwanted extra lines

8.3 Writing out data files and plots

8.3.1 Data Sets

Use the write.table function

write.table(x=mydatafile, file="D:\\Dropbox\\BigData\\R Programming\\Work\\bookofr\\somenewfile.txt",
            sep="@",
            na="??",
            quote=FALSE,
            row.names=FALSE)

The output file will have contents like this:

person@age@sex@funny@age.mon Peter@??@M@High@504 Lois@40@F@??@480 Meg@17@F@Low@204 Chris@14@M@Med@168 Stewie@1@M@High@?? Brian@??@M@Med@??

See also read.csv and write.csv which are shortcut versions of read.table and write.table.

8.3.2 Plot and Graphics Files

YOu can save plots using either: jpeg pdf eps ggsave

jpeg(filename="D:\\Dropbox\\BigData\\R Programming\\Work\\bookofr\\someplot.jpeg",
     width=600,height=600
     )
plot(1:5,10:6,ylab="Y axis", xlab=" x axis",
     main="A save jpeg plot")
points(1:5,10:6,cex=2,pch=4, col=2)
dev.off()
null device 
          1 
pdf(file="D:\\Dropbox\\BigData\\R Programming\\Work\\bookofr\\someplot.pdf",
     width=5,height=5
     )
plot(1:5,10:6,ylab="Y axis", xlab=" x axis",
     main="A save jpeg plot")
points(1:5,10:6,cex=2,pch=4, col=2)
dev.off()
null device 
          1 
library("ggplot2")
foo
[1] 1.1 2.0 3.5 3.9 4.2
bar
[1]  2.0  2.2 -1.3  0.0  0.2
qplot(foo,bar,geom="blank")+
  geom_point(size=3,shape=8,color="darkgreen")+
  geom_line(color="orange",linetype=4)
# now save the plot using ggsave
ggsave(filename="D:\\Dropbox\\BigData\\R Programming\\Work\\bookofr\\myqplot.png")
Saving 7.29 x 4.5 in image
# you can save into different formats by simply changing the extension
ggsave(filename="D:\\Dropbox\\BigData\\R Programming\\Work\\bookofr\\myqplot.pdf")
Saving 7.29 x 4.5 in image

To find our more details: ?pdf ?postscript ?jpeg ?ggsave

8.4 Ad Hoc Object Read/Write Operations

Use dput and dget

somelist<-list(foo=c(5,2,45),
               bar=matrix(data=c(T,T,F,F,F,F,T,F,T), nrow=3,ncol=3),
               baz=factor(c(1,2,2,3,1,1,3),levels=1:3,ordered=T))
somelist
$foo
[1]  5  2 45

$bar
      [,1]  [,2]  [,3]
[1,]  TRUE FALSE  TRUE
[2,]  TRUE FALSE FALSE
[3,] FALSE FALSE  TRUE

$baz
[1] 1 2 2 3 1 1 3
Levels: 1 < 2 < 3
# put object somelist into file
dput(x=somelist, file="D:\\Dropbox\\BigData\\R Programming\\Work\\bookofr\\myRobject.txt")

Now to read back the object

newobect<-dget(file="D:\\Dropbox\\BigData\\R Programming\\Work\\bookofr\\myRobject.txt")
newobect
$foo
[1]  5  2 45

$bar
      [,1]  [,2]  [,3]
[1,]  TRUE FALSE  TRUE
[2,]  TRUE FALSE FALSE
[3,] FALSE FALSE  TRUE

$baz
[1] 1 2 2 3 1 1 3
Levels: 1 < 2 < 3
LS0tDQp0aXRsZTogIkNoYXB0ZXIgOCBSZWFkaW5nIGFuZCBXcml0aW5nIEZpbGVzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KPEgxPlJlYWRpbmcgYW5kIFdyaXRpbmcgRmlsZXMgPC9IMT4NClRoaXMgY292ZXJzIGxvYWRpbmcgYW5kIHNhdmluZyBkYXRhIGluIGFuIGFjdGl2ZSB3b3Jrc3BhY2UgYnkgcmVhZGluZyBhbmQgd3JpdGluZyBmaWxlcy4NCg0KPGgyPiA4LjEgUi1SZWFkeSBEYXRhIFNldHM8L2gyPg0KRW50ZXIgZGF0YSgpIGF0IHRoZSBSIHByb21wdCB0byBicmluZyB1cCBhIHdpbmRvdyBsaXN0aW5nIHRoZXNlIHJlYWR5LXRvLXVzZSBkYXRhIHNldHMgYWxvbmcgd2l0aCBhIG9uZS1saW5lIGRlc2NyaXB0aW9uDQoNCmBgYHtyfQ0KZGF0YSgpDQpgYGANCg0KVXNlID8/P2RhdGEocGFja2FnZSA9IC5wYWNrYWdlcyhhbGwuYXZhaWxhYmxlID0gVFJVRSkpPz8/DQp0byBsaXN0IHRoZSBkYXRhIHNldHMgaW4gYWxsICphdmFpbGFibGUqIHBhY2thZ2VzLg0KDQoNCjxoMz4gOC4xLjEgQnVpbHQtaW4gRGF0YSBzZXRzPC9oMz4NClRvIHNlZSBhIHN1bW1hcnkgb2YgdGhlIGRhdGEgc2V0cyBjb250YWluZWQgaW4gdGhlIHBhY2thZ2UsIHlvdSBjYW4gdXNlIHRoZSBsaWJyYXJ5IGZ1bmN0aW9uIGxpYnJhcnkoaGVscD0iZGF0YXNldHMiKQ0KDQpgYGB7cn0NCmxpYnJhcnkoaGVscD0iZGF0YXNldHMiKQ0KYGBgDQoNCg0KYGBge3J9DQo/Q2hpY2tXZWlnaHQNCmBgYA0KDQoNCiAgICAgICANCmBgYHtyfQ0KQ2hpY2tXZWlnaHRbMToxNSxdDQoNCmBgYA0KDQogICAgICAgICAgICAgICAgICAgICAgICANCjxoMz4gOC4xLjIgQ29udHJpYnV0ZWQgRGF0YSBTZXRzPC9oMz4NCkFkZGl0aW9uYWwgZGF0YSBzZXRzIGFyZSBhbHNvIGF2YWlsYWJsZSBhcyBjb250cmlidXRlZCBwYWNrYWdlcy4NClRvIGFjY2VzcyB0aGVtLCBmaXJzdCBpbnN0YWxsIGFuZCBsb2FkIHRoZSByZWxldmFudCBwYWNrYWdlLg0KQ29uc2lkZXIgdGhlIGRhdGEgc2V0IGljZS5yaXZlciB3aGljaCBpcyBpbiB0aGUgY29udHJpdWJ0ZWQgcGFja2FnZSB0c2VyaWVzIGJ5IFRyYXBsZXR0aSBhbmQgSG9ybmlrKDIwMTMpDQoNCkZpcnN0IGluc3RhbGwgdGhlIHBhY2thZ2UgYnkgcnVubmluZyB0aGUgbGluZSAgaW5zdGFsbC5wYWNrYWdlcygidHNlcmllcyIpIGF0IHRoZSBwcm9tcHQuDQpUaGVuLCB0byBhY2Nlc3MgdGhlIGNvbXBvbmVudHMgb2YgdGhlIHBha2NhZ2UsIGxvYWQgaXQgdXNpbmcgbGlicmFyeQ0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJ0c2VyaWVzIg0KICAgICAgICAgICAgICAgICApDQoNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoInRzZXJpZXMiKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShoZWxwPSJ0c2VyaWVzIikNCmBgYA0KDQpZb3UgY2FuIGVudGVyID9pY2Uucml2ZXIgdG8gZmluZCBtb3JlIGRldGFscyBhYm91dCB0aCBkYXRhIHNldCB5b3Ugd2FudCB0byB3b3JrIC4NClRoZSBoZWxwIGZpbGUgZGVzY3JpYmVzIGl0IGFzIGEgInRpbWUgc2VyaWVzIG9iamVjdCIgY29tcHJyaXNlZCBvZiByaXZlciBmbG93LCBwcmVjaXBpdGF0aW9uLCBhbmQgdGVtcGVyYXR1cmUgbWVhc3VyZW1lbnRzLiBEYXRhIGluaXRpYWxseSByZXBvcnRlZCBpbiBUb25nICgxOTkwKQ0KDQpgYGB7cn0NCj9pY2Uucml2ZXINCg0KYGBgDQpJY2VsYW5kaWMgUml2ZXIgRGF0YQ0KDQpEZXNjcmlwdGlvbg0KDQpDb250YWlucyB0aGUgSWNlbGFuZGljIHJpdmVyIGRhdGEgYXMgcHJlc2VudGVkIGluIFRvbmcgKDE5OTApLCBwYWdlcyA0MzI/Pz80NDAuDQoNClVzYWdlDQoNCmRhdGEoaWNlLnJpdmVyKQ0KRm9ybWF0DQoNCjQgdW5pdmFyaWF0ZSB0aW1lIHNlcmllcyBmbG93LnZhdCwgZmxvdy5qb2ssIHByZWMsIGFuZCB0ZW1wLCBlYWNoIHdpdGggMTA5NSBvYnNlcnZhdGlvbnMgYW5kIHRoZSBqb2ludCBzZXJpZXMgaWNlLnJpdmVyLg0KDQpEZXRhaWxzDQoNClRoZSBzZXJpZXMgYXJlIGRhaWx5IG9ic2VydmF0aW9ucyBmcm9tIEphbi4gMSwgMTk3MiB0byBEZWMuIDMxLCAxOTc0IG9uIDQgdmFyaWFibGVzOiBmbG93LnZhdCwgbWVhbiBkYWlseSBmbG93IG9mIFZhdG5zZGFsc2Egcml2ZXIgKGNtcyksIGZsb3cuam9rLCBtZWFuIGRhaWx5IGZsb3cgb2YgSm9rdWxzYSBFeXN0cmkgcml2ZXIgKGNtcyksIHByZWMsIGRhaWx5IHByZWNpcGl0YXRpb24gaW4gSHZlcmF2ZWxsaXIgKG1tKSwgYW5kIG1lYW4gZGFpbHkgdGVtcGVyYXR1cmUgaW4gSHZlcmF2ZWxsaXIgKGRlZyBDKS4NCg0KVGhlc2UgZGF0YXNldHMgd2VyZSBpbnRyb2R1Y2VkIGludG8gdGhlIGxpdGVyYXR1cmUgaW4gYSBwYXBlciBieSBUb25nLCBUaGFub29uLCBhbmQgR3VkbXVuZHNzb24gKDE5ODUpLg0KDQoNCmBgYHtyfQ0KZGF0YShpY2Uucml2ZXIpDQppY2Uucml2ZXJbMTo1LF0NCmBgYA0KDQo8aDI+IDguMiBSZWFkaW5nIGluIEV4dGVybmFsIERhdGEgRmlsZXM8L2gyPg0KWW91IG9mdGVuIGhhdmUgdG8gd29yayB3aXRoIGRhdGEgZnJvbSBleHRlcm5hbCBzb3VyY2VzLiANClRoaXMgc2hvd3MgaG93IHRvIGRvIHRoYXQuDQoNCjxoMz4gOC4yLjEgVGhlIFRhYmxlIGZvcm1hdDwvaDM+DQpUYWJsZSBmb3JtYXQgZmlsZXMgaGF2ZSAzIGtleSBmZWF0dXJlczoNCkhlYWRlciAocHJvdmlkZXMgbmFtZXMgZm9yIGVhY2ggY29sdW1uIG9mIGRhdGEpDQpEZWxpbWl0ZXIgKGNoYXJhY3RlciB1c2VkIHRvIHNlcGFyYXRlIHRoZSBlbnRyaWVzIGluIGVhY2ggbGluZSkNCk1pc3NpbmcgVmFsdWUgKGNoYXJhY3RlciBzdHJpbmcgdXNlZCB0byBleGNsdXNpdmVseSBkZW5vdGUgYSBtaXNzaW5nIHZhbHVlKQ0KDQpUeXBpY2FsbHkgdGhlc2UgZmlsZXMgaGF2ZSBhIC50eHQgZXh0ZW5zaW9uIG9yIC5jc3YNCkRvd25sb2FkIHRoZSBzYW1wbGUgZmlsZXMgZnJvbToNCmh0dHBzOi8vd3d3Lm5vc3RhcmNoLmNvbS9ib29rb2ZyDQoNCg0KDQpgYGB7cn0NCm15ZGF0YWZpbGU8LSByZWFkLnRhYmxlKGZpbGU9ImQ6L2Jvb2tvZnIvOC4yLjFfbXlkYXRhZmlsZS50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICBzZXA9IiAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncz0iKiIsDQogICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQpteWRhdGFmaWxlDQpgYGANClRpcDogaWYgeW91IGFyZSB3b3JraW5nIHdpdGggbXVsdGlwbGUgZmlsZXMgYW5kIGRvbid0IHdhbnQgdG8gc3BlY2lmeSB0aGUgd29ya2luZyBkaXJlY3RvcnksDQp5b3UgY2FuIHNldCBpdCB3aXRoIHRoZSBzZXR3ZCgpIGNvbW1hbmQNCmBgYHtyfQ0KIyBnZXQgd29ya2luZyBkaXJlY3RvcnkNCmdldHdkKCkNCiMgY2hhbmdlIGl0IGJhY2sgdG8gb3JpZ2luYWwgc2V0dGluZw0Kc2V0d2QoIkQ6L0Ryb3Bib3gvQmlnRGF0YS9SIFByb2dyYW1taW5nL1dvcmsiKQ0KZ2V0d2QoKQ0KDQpgYGANCmBgYHtyfQ0KbGlzdC5maWxlcygiRDovRHJvcGJveC9CaWdEYXRhL1IgUHJvZ3JhbW1pbmcvV29yay9ib29rb2ZyIikNCmBgYA0KDQpZb3UgY2FuIGFsc28gZmluZCBmaWxlcyBpbnRlcmFjdGl2ZWx5IHVzaW5nIHRoZSANCmZpbGUuY2hvb3NlIGNvbW1hbmQNCg0KYGBge3J9DQpmaWxlLmNob29zZSgpDQoNCmBgYA0KDQpTbyB5b3UgY2FuIGNvbWJpbmUgdGhlIG91dHB1dCBvZiB0aGUgYWJvdmUgY29tbWFuZCB0byBsb2FkIHRoZSBmaWxlLg0KDQpgYGB7cn0NCm15ZGF0YWZpbGU8LSByZWFkLnRhYmxlKGZpbGU9ZmlsZS5jaG9vc2UoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIgIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5hLnN0cmluZ3M9IioiLA0KICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KbXlkYXRhZmlsZQ0KYGBgDQpOb3JtYWxseSwgDQpyZWFkLnRhYmxlIHdpbGwgY29udmVydCBub24tbnVtZXJpYyB2YWx1ZXMgaW50byBmYWN0b3JzIGJ5IGRlZmF1bHQuIA0KVG8gcHJldmVudCB0aGlzLCB3ZSB1c2UgdGhlICJzdHJpbmdzQXNGYWN0b3JzPUZBTFNFIg0KVG8gb3ZlcmlkZSBvbmUgb2YgdGhlbSBhcyB0aGF0IGRhdGEgdHlwZToNCg0KYGBge3J9DQojIGxvb2sgYXQgdGhlIGN1cnJlbnQgbXlkYXRhZmlsZSBiZWZvcmUgZGF0YSB0eXBlIG92ZXJpZGUNCm15ZGF0YWZpbGUNCiMgbm93IHdlIG92ZXJpZGUgdGhlbQ0KbXlkYXRhZmlsZSRzZXggPC0gYXMuZmFjdG9yKG15ZGF0YWZpbGUkc2V4KQ0KbXlkYXRhZmlsZSRmdW5ueSA8LSBmYWN0b3IoeD1teWRhdGFmaWxlJGZ1bm55LGxldmVscz1jKCJMb3ciLCJNZWQiLCJIaWdoIikpDQpteWRhdGFmaWxlDQoNCmBgYA0KDQo8aDM+IDguMi4yIFNwcmVhZHNoZWV0IFdvcmtib29rczwvaDM+DQoNCkdlbmVyYWxseSB5b3Ugd2lsbCBuZWVkIHRvIHVzZSBmaWxlIHNhdmUgYXMgYW5kIHNhdmUgdGhlIHhscyBvciB4bHN4IGludG8gLmNzdiBmb3JtYXQuDQpZb3UgY2FuIGFsc28gdXNlIHNvbWUgY29udHJpYnV0ZWQgcGFja2FnZXMgbGlrZSBnZGF0YSBieSBXYXJuZXMgZXQgYWwuIG9yIFhMQ29ubmVjdCBieSBNaXJhaSBTb2x1dGlvbnMNCg0KPGgzPiA4LjIuMyBXZWIgQmFzZWQgRmlsZXM8L2gzPg0KWW91IHVzZSB0aGUgc2FtZSByZWFkLnRhYmxlIGNvbW1hbmQsIGJ1dCB0aGlzIHRpbWUgaW4gdGhlIGZpbGU9IiIgYXJlYSwgdXNlIHRoZSBVUkwgDQoNCmBgYHtyfQ0KZGlhLnVybDwtImh0dHA6Ly93d3cuYW1zdGF0Lm9yZy9wdWJsaWNhdGlvbnMvanNlL3Y5bjIvNGNkYXRhLnR4dCINCmRpYW1vbmRzIDwtcmVhZC50YWJsZShkaWEudXJsKQ0KZGlhbW9uZHMNCiMgbm93IGFkZCB0aGUgaGVhZGVyIHVzaW5nIHRoZSBuYW1lcyBmdW5jdGlvbg0KDQpuYW1lcyhkaWFtb25kcyk8LWMoIkNhcmF0IiwiQ29sb3IiLCJDbGFyaXR5IiwiQ2VydCIsIlByaWNlIikNCiMgbm93IGxpc3QgdGhlIHRhYmxlDQpkaWFtb25kc1sxOjUsXQ0KYGBgDQoNCjxoMz4gOC4yLjQgT3RoZXIgRmlsZSBGb3JtYXRzPC9oMz4NCk90aGVyIGZvcm1hdCBsaWtlIC5kYXQgY2FuIGJlIGltcG9ydGVkIHVzaW5nIHJlYWQudGFibGUuIA0KVXNlIHRoZSBza2lwIGFyZ3VtZW50IHRvIHJlbW92ZSB0aGUgdW53YW50ZWQgZXh0cmEgbGluZXMNCg0KPGgyPiA4LjMgV3JpdGluZyBvdXQgZGF0YSBmaWxlcyBhbmQgcGxvdHM8L2gyPg0KDQo8aDM+IDguMy4xIERhdGEgU2V0czwvaDM+DQpVc2UgdGhlIHdyaXRlLnRhYmxlIGZ1bmN0aW9uDQpgYGB7cn0NCndyaXRlLnRhYmxlKHg9bXlkYXRhZmlsZSwgZmlsZT0iRDpcXERyb3Bib3hcXEJpZ0RhdGFcXFIgUHJvZ3JhbW1pbmdcXFdvcmtcXGJvb2tvZnJcXHNvbWVuZXdmaWxlLnR4dCIsDQogICAgICAgICAgICBzZXA9IkAiLA0KICAgICAgICAgICAgbmE9Ij8/IiwNCiAgICAgICAgICAgIHF1b3RlPUZBTFNFLA0KICAgICAgICAgICAgcm93Lm5hbWVzPUZBTFNFKQ0KYGBgDQpUaGUgb3V0cHV0IGZpbGUgd2lsbCBoYXZlIGNvbnRlbnRzIGxpa2UgdGhpczoNCg0KcGVyc29uQGFnZUBzZXhAZnVubnlAYWdlLm1vbg0KUGV0ZXJAPz9ATUBIaWdoQDUwNA0KTG9pc0A0MEBGQD8/QDQ4MA0KTWVnQDE3QEZATG93QDIwNA0KQ2hyaXNAMTRATUBNZWRAMTY4DQpTdGV3aWVAMUBNQEhpZ2hAPz8NCkJyaWFuQD8/QE1ATWVkQD8/DQoNClNlZSBhbHNvIHJlYWQuY3N2IGFuZCB3cml0ZS5jc3Ygd2hpY2ggYXJlIHNob3J0Y3V0IHZlcnNpb25zIG9mIHJlYWQudGFibGUgYW5kIHdyaXRlLnRhYmxlLg0KDQoNCjxoMz4gOC4zLjIgUGxvdCBhbmQgR3JhcGhpY3MgRmlsZXM8L2gzPg0KWU91IGNhbiBzYXZlIHBsb3RzIHVzaW5nIGVpdGhlcjoNCmpwZWcNCnBkZg0KZXBzDQpnZ3NhdmUNCg0KYGBge3J9DQpqcGVnKGZpbGVuYW1lPSJEOlxcRHJvcGJveFxcQmlnRGF0YVxcUiBQcm9ncmFtbWluZ1xcV29ya1xcYm9va29mclxcc29tZXBsb3QuanBlZyIsDQogICAgIHdpZHRoPTYwMCxoZWlnaHQ9NjAwDQogICAgICkNCnBsb3QoMTo1LDEwOjYseWxhYj0iWSBheGlzIiwgeGxhYj0iIHggYXhpcyIsDQogICAgIG1haW49IkEgc2F2ZSBqcGVnIHBsb3QiKQ0KcG9pbnRzKDE6NSwxMDo2LGNleD0yLHBjaD00LCBjb2w9MikNCmRldi5vZmYoKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiMgcGRmIHVzZXMgZmlsZSBpbnN0ZWFkIG9mIGZpbGVuYW1lDQojIHBkZiB1c2VzIGluY2hlcyBpbnN0ZWFkIG9mIHBpeGVscw0KcGRmKGZpbGU9IkQ6XFxEcm9wYm94XFxCaWdEYXRhXFxSIFByb2dyYW1taW5nXFxXb3JrXFxib29rb2ZyXFxzb21lcGxvdC5wZGYiLA0KICAgICB3aWR0aD01LGhlaWdodD01DQogICAgICkNCnBsb3QoMTo1LDEwOjYseWxhYj0iWSBheGlzIiwgeGxhYj0iIHggYXhpcyIsDQogICAgIG1haW49IkEgc2F2ZSBqcGVnIHBsb3QiKQ0KcG9pbnRzKDE6NSwxMDo2LGNleD0yLHBjaD00LCBjb2w9MikNCmRldi5vZmYoKQ0KDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmZvbw0KYmFyDQpxcGxvdChmb28sYmFyLGdlb209ImJsYW5rIikrDQogIGdlb21fcG9pbnQoc2l6ZT0zLHNoYXBlPTgsY29sb3I9ImRhcmtncmVlbiIpKw0KICBnZW9tX2xpbmUoY29sb3I9Im9yYW5nZSIsbGluZXR5cGU9NCkNCiMgbm93IHNhdmUgdGhlIHBsb3QgdXNpbmcgZ2dzYXZlDQpnZ3NhdmUoZmlsZW5hbWU9IkQ6XFxEcm9wYm94XFxCaWdEYXRhXFxSIFByb2dyYW1taW5nXFxXb3JrXFxib29rb2ZyXFxteXFwbG90LnBuZyIpDQojIHlvdSBjYW4gc2F2ZSBpbnRvIGRpZmZlcmVudCBmb3JtYXRzIGJ5IHNpbXBseSBjaGFuZ2luZyB0aGUgZXh0ZW5zaW9uDQpnZ3NhdmUoZmlsZW5hbWU9IkQ6XFxEcm9wYm94XFxCaWdEYXRhXFxSIFByb2dyYW1taW5nXFxXb3JrXFxib29rb2ZyXFxteXFwbG90LnBkZiIpDQpgYGANClRvIGZpbmQgb3VyIG1vcmUgZGV0YWlsczoNCj9wZGYNCj9wb3N0c2NyaXB0DQo/anBlZw0KP2dnc2F2ZQ0KDQoNCjxoMj4gOC40IEFkIEhvYyBPYmplY3QgUmVhZC9Xcml0ZSBPcGVyYXRpb25zPC9oMj4NClVzZSBkcHV0IGFuZCBkZ2V0DQpgYGB7cn0NCnNvbWVsaXN0PC1saXN0KGZvbz1jKDUsMiw0NSksDQogICAgICAgICAgICAgICBiYXI9bWF0cml4KGRhdGE9YyhULFQsRixGLEYsRixULEYsVCksIG5yb3c9MyxuY29sPTMpLA0KICAgICAgICAgICAgICAgYmF6PWZhY3RvcihjKDEsMiwyLDMsMSwxLDMpLGxldmVscz0xOjMsb3JkZXJlZD1UKSkNCnNvbWVsaXN0DQoNCg0KDQpgYGANCmBgYHtyfQ0KIyBwdXQgb2JqZWN0IHNvbWVsaXN0IGludG8gZmlsZQ0KZHB1dCh4PXNvbWVsaXN0LCBmaWxlPSJEOlxcRHJvcGJveFxcQmlnRGF0YVxcUiBQcm9ncmFtbWluZ1xcV29ya1xcYm9va29mclxcbXlSb2JqZWN0LnR4dCIpDQpgYGANCg0KTm93IHRvIHJlYWQgYmFjayB0aGUgb2JqZWN0IA0KYGBge3J9DQpuZXdvYmVjdDwtZGdldChmaWxlPSJEOlxcRHJvcGJveFxcQmlnRGF0YVxcUiBQcm9ncmFtbWluZ1xcV29ya1xcYm9va29mclxcbXlSb2JqZWN0LnR4dCIpDQpuZXdvYmVjdA0KDQpgYGANCg0K