General Instructions

Aims of the Lab

In this lab you will learn and practice how to load data from various source in to R. In particular we will cover the following objectives:

1. Working with Files or URLs

Structured data from a file/URL can be imported into R using an R table . More information about the table() function is at https://stat.ethz.ch/R-manual/R-devel/library/base/html/table.html.

Tip for getting help in R: When you want to access a help page from the console type ?nameofthefunction E.g. >?table will show the help page for R table in base package. Alternatively you can search the topic in the search bar on the help pane in R studio.

1.1 Working with Structured Data

  • Q1 Download the ‘lab1.zip’ folder from url and extract it. Open the ‘lab1-ex.R’ in RStudio. Set the working directory(https://stat.ethz.ch/R-manual/R-devel/library/base/html/getwd.html) to the extracted folder. Press ‘ALT+CRTL+R’ (this is the shortcut to execture an entire R script in RStudio). If there are no errors you can start on Q2, otherwise check the path you have assigned to the working directory. Note: paths in windows platforms are different to unix (mac or linux) platforms.

1.1.1 Familiarise with a dataset

  • Q2 Find the ‘car.data.csv’ file in the ‘data’ subfolder supplied with Lab 1. Open the file with Microsoft Excel (Please do not perform any suggested saving options by MS Excel). Note how the data and headers are given. Close the file. If you are in Mac environment, follow the steps in a spreadsheet application.

  • Q3 Open the ‘car.data.csv’ file with Microsoft Wordpad. Do you notice any difference in the view?

  • Q4 Create a duplicate of the file ‘car.data.csv’ and save it as ‘manuallyedit.car.data.csv’. Open this file change the first column name from ‘buying’ to ‘Buying’. Save the changes and close the file.

  • Q5 Categorize the data types in the file as quantitative or qualitiative.

1.1.2 Importing data into R

  • Q6 Use the following code snippet to load the content of the ‘car.data.csv’ into an R table data structure. Although the URL works, you have to provide the path to the data file in your code.
uciCar=read.table(file='http://www.win-vector.com/dfiles/car.data.csv',header = TRUE,sep = ',')
  • Q7 Import the same dataset into a different variable ‘uciCarWr’using the read.table() funnction. Set ’FALSE’ to the header parameter.

1.1.3 Examining the imported data

  • Q8 Use dim function to get the dimensions of the dataset. How many data entries are there in the dataset?

  • Q9 How many rows are there in the ‘uciCarWr’? Why the answer is different to the answer from Q7

  • Q10 Use the ‘class()’ function to find the type of the variables in ‘uciCar’. E.g. class(uciCar$buying) will return the type of the ‘buying’ variable. Are they all different or same? Write your observation as comment

  • Q11 Enter the following command. Describe the meaning of entries under ‘doors’.

summary(uciCar)
   buying      maint       doors     persons     lug_boot    safety      rating    
 high :432   high :432   2    :432   2   :576   big  :576   high:576   acc  : 384  
 low  :432   low  :432   3    :432   4   :576   med  :576   low :576   good :  69  
 med  :432   med  :432   4    :432   more:576   small:576   med :576   unacc:1210  
 vhigh:432   vhigh:432   5more:432                                     vgood:  65  

1.1.4 Working with other data formats

R has many adaptors that can be used to import data from a variety of sources.

1.2 Working with Less-Structured Data

Not all data are stored in a structured manner. Often data scientists use scripts to manipulate imported data to impose a structure, so that the data can be analysed conveniently.

  • Q12 Go to the URL ‘http://archive.ics.uci.edu/ml/datasets/Statlog+(German+Credit+Data)’ which hosts the German Bank Credit Dataset. Download the dataset into your working directory’s data folder.

  • Q13 Use the following command to load the data into a variable called ‘d’. Use the downloaded file path and type in your code. What are the column names?

d <- read.table(paste('http://archive.ics.uci.edu/ml/',
'machine-learning-databases/statlog/german/german.data',sep=''),stringsAsFactors=F,header=F)
  • Q14 Change the column names of the dataset using the ‘colnames()’ function as follows. The ‘c()’ command construct a vector that contains the desired column names.
colnames(d) <- c('Status.of.existing.checking.account',
'Duration.in.month', 'Credit.history', 'Purpose',
'Credit.amount', 'Savings account/bonds',
'Present.employment.since','Installment.rate.in.percentage.of.disposable.income',
'Personal.status.and.sex', 'Other.debtors/guarantors',
'Present.residence.since', 'Property', 'Age.in.years',
'Other.installment.plans', 'Housing',
'Number.of.existing.credits.at.this.bank', 'Job',
'Number.of.people.being.liable.to.provide.maintenance.for',
'Telephone', 'foreign.worker', 'Good.Loan')

Using a data dictionary or a schema documentation you can transform data in R. A list in R can be used to create such a mapping structure. For example, meaning of the A-* codes can be decoded using a list like below.

mapping <- list(
'A40'='car (new)',
'A41'='car (used)',
'A42'='furniture/equipment',
'A43'='radio/television',
'A44'='domestic appliances'
)

The’ actual data can be decoded using the ‘mapping’ list as follows.

for(i in 1:(dim(d))[2]) {
    if(class(d[,i])=='character') {
        d[,i] <- as.factor(as.character(mapping[d[,i]]))
    }
}
  • Q15 Use the above code to transform the data. Then, use the following code to view the data
table(d$Purpose,d$Good.Loan)
                     
                        1   2
  car (new)           145  89
  car (used)           86  17
  domestic appliances   8   4
  furniture/equipment 123  58
  NULL                120  70
  radio/television    218  62

2. Working with Relational Databases

Relational databases can scale upto millions of records. And structured queries can be feteched easily with relational databases. In the following sections, we will examine, how to get data from a database into R for analysis.

2.1 Curating the Data

You need to record the source of the data properly. Go to the bereau of meteorology and get download climate data for july 2016 and july 2017 months.

  • Q16 Create a data provonence documentation for this dataset. Your note should include data access date, source url, how you navigate the data in the given url, data format and cryptographic hashes. ( Hint: You can find a similar document in chapter 2 of the reference book)

2.3 Loading Data from a Database into R

  • Q17 Explore the ‘julyclimate.db’ database in the ‘data/db’ folder using an SQL database viewer. How many records are there in the dataset?

  • Q18 Using the following code, import the data in the database into R.

require(DBI)
require(RSQLite)
DBFILENAME<-'C:\\lab1\\data\\db\\julyclimate.db'
thisdb <- dbConnect(RSQLite::SQLite(), DBFILENAME)
start_date <-'01-07-2016'
end_date   <-'31-07-2016'
#query = sprintf("SELECT * FROM july16 WHERE Date BETWEEN '%s' AND '%s'",start_date,end_date)
query= 'SELECT * FROM july16'
rawdata<-dbGetQuery(thisdb,query)
dbDisconnect(thisdb)
[1] TRUE
  • Q19 Open the database in your working folders (‘data.db’) in the database viewer. Find the other tables. Change the above code to import the july 2017 data into R and save it in ‘july17’ variable. Also change the variable name ‘rawdata’ as ‘july16’
july16<-rawdata
  • Q20 You can select a subset of data by manipulating the data frames (this is the data structure name). Execute the following code to find the days with minimum temperatures
mintemp_jul16<-head(sort(july16$`Minimumtemperature(�C)`))
mintemp_jul16
[1] "0.6"  "1.2"  "10.1" "10.6" "10.8" "10.9"
  • Q21 Find the lowest (minimum 5) temperatures in july 2017 data. Save the results in mintemp_july17 variable. Are there any variations in the minimum temperatures?

  • Q22 You can get a quick overview of a dataset using the ‘summary’ function. Explore information given by the summary function.

 summary(july16)
     Date           Minimumtemperature(�C) Maximumtemperature(�C) Rainfall(mm)      
 Length:31          Length:31                Length:31                Length:31         
 Class :character   Class :character         Class :character         Class :character  
 Mode  :character   Mode  :character         Mode  :character         Mode  :character  
 Evaporation(mm)    Sunshine(hours)    Directionofmaximumwindgust
 Length:31          Length:31          Length:31                 
 Class :character   Class :character   Class :character          
 Mode  :character   Mode  :character   Mode  :character          
 Speedofmaximumwindgust(km/h) Timeofmaximumwindgust 9amTemperature(�C)
 Length:31                    Length:31             Length:31           
 Class :character             Class :character      Class :character    
 Mode  :character             Mode  :character      Mode  :character    
 9amrelativehumidity(%) 9amcloudamount(oktas) 9amwinddirection   9amwindspeed(km/h)
 Length:31              Length:31             Length:31          Length:31         
 Class :character       Class :character      Class :character   Class :character  
 Mode  :character       Mode  :character      Mode  :character   Mode  :character  
 9amMSLpressure(hPa) 3pmTemperature(�C) 3pmrelativehumidity(%) 3pmcloudamount(oktas)
 Length:31           Length:31            Length:31              Length:31            
 Class :character    Class :character     Class :character       Class :character     
 Mode  :character    Mode  :character     Mode  :character       Mode  :character     
 3pmwinddirection   3pmwindspeed(km/h) 3pmMSLpressure(hPa)
 Length:31          Length:31          Length:31          
 Class :character   Class :character   Class :character   
 Mode  :character   Mode  :character   Mode  :character   
DQotLS0NCnRpdGxlOiAiQ0lUUzQwMDkgTGFiIDIgLSBMb2FkaW5nIERhdGEgaW50byBSIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMjIEdlbmVyYWwgSW5zdHJ1Y3Rpb25zDQoqIFlvdXIgbGFic2hlZXRzIHdpbGwgYmUgc3RydWN0dXJlZCB3aXRoIGNvbXBsZW1lbnRvcnkgaW5mb3JtYXRpb24uIFRoZSBsYWJzIHdpbGwgY2xvc2VseSBmb2xsb3cgdGhlIHN0cnVjdHVyZSBvZiAiUHJhY3RpY2FsIERhdGEgU2NpZW5jZSB3aXRoIFIiIGJvb2sgYnkgTmluYSBadW1lbCBhbmQgSm9obiBNb3VudCANCiogRnJvbSBlYWNoIGxhYiB5b3UgYXJlIGV4cGVjdGVkIHRvIGFuc3dlciBhbGwgdGhlIHF1ZXN0aW9ucyBwcmVzZW50ZWQgd2l0aCBhIHF1ZXN0aW9uIG51bWJlci4gDQoNCg0KIyMjIEFpbXMgb2YgdGhlIExhYg0KSW4gdGhpcyBsYWIgeW91IHdpbGwgbGVhcm4gYW5kIHByYWN0aWNlIGhvdyB0byBsb2FkIGRhdGEgZnJvbSB2YXJpb3VzIHNvdXJjZSBpbiB0byBSLiBJbiBwYXJ0aWN1bGFyDQp3ZSB3aWxsIGNvdmVyIHRoZSBmb2xsb3dpbmcgb2JqZWN0aXZlczoNCg0KKiBVbmRlcnN0YW5kaW5nIFLigJlzIGRhdGEgZnJhbWUgc3RydWN0dXJlDQoqIExvYWRpbmcgZGF0YSBpbnRvIFIgZnJvbSBmaWxlcyBhbmQgZnJvbSByZWxhdGlvbmFsIGRhdGFiYXNlcw0KKiBUcmFuc2Zvcm1pbmcgZGF0YSBmb3IgYW5hbHlzaXMNCg0KDQojIDEuIFdvcmtpbmcgd2l0aCBGaWxlcyBvciBVUkxzDQpTdHJ1Y3R1cmVkIGRhdGEgZnJvbSBhIGZpbGUvVVJMIGNhbiBiZSBpbXBvcnRlZCBpbnRvIFIgdXNpbmcgYW4gKipSIHRhYmxlKiogLiBNb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSAqKnRhYmxlKCkqKiBmdW5jdGlvbiBpcyBhdCAgaHR0cHM6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2Jhc2UvaHRtbC90YWJsZS5odG1sLiANCg0KVGlwIGZvciBnZXR0aW5nIGhlbHAgaW4gUjogV2hlbiB5b3Ugd2FudCB0byBhY2Nlc3MgYSBoZWxwIHBhZ2UgZnJvbSB0aGUgY29uc29sZSB0eXBlID9uYW1lb2Z0aGVmdW5jdGlvbiBFLmcuID4/dGFibGUgd2lsbCBzaG93IHRoZSBoZWxwIHBhZ2UgZm9yIFIgdGFibGUgaW4gYmFzZSBwYWNrYWdlLiBBbHRlcm5hdGl2ZWx5IHlvdSBjYW4gc2VhcmNoIHRoZSB0b3BpYyBpbiB0aGUgc2VhcmNoIGJhciBvbiB0aGUgaGVscCBwYW5lIGluIFIgc3R1ZGlvLg0KDQojIyAxLjEgV29ya2luZyB3aXRoIFN0cnVjdHVyZWQgRGF0YQ0KKiAqKlExKiogRG93bmxvYWQgdGhlICdsYWIxLnppcCcgZm9sZGVyIGZyb20gKnVybCogYW5kIGV4dHJhY3QgaXQuIE9wZW4gdGhlICdsYWIxLWV4LlInIGluIFJTdHVkaW8uIFNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkoaHR0cHM6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2Jhc2UvaHRtbC9nZXR3ZC5odG1sKSB0byB0aGUgZXh0cmFjdGVkIGZvbGRlci4gUHJlc3MgJ0FMVCtDUlRMK1InICh0aGlzIGlzIHRoZSBzaG9ydGN1dCB0byBleGVjdHVyZSBhbiBlbnRpcmUgUiBzY3JpcHQgaW4gUlN0dWRpbykuIElmIHRoZXJlIGFyZSBubyBlcnJvcnMgeW91IGNhbiBzdGFydCBvbiAqKlEyKiosIG90aGVyd2lzZSBjaGVjayB0aGUgcGF0aCB5b3UgaGF2ZSBhc3NpZ25lZCB0byB0aGUgd29ya2luZyBkaXJlY3RvcnkuIE5vdGU6IHBhdGhzIGluIHdpbmRvd3MgcGxhdGZvcm1zIGFyZSBkaWZmZXJlbnQgdG8gdW5peCAobWFjIG9yIGxpbnV4KSBwbGF0Zm9ybXMuDQoNCiMjIyAxLjEuMSBGYW1pbGlhcmlzZSB3aXRoIGEgZGF0YXNldA0KDQoqICoqUTIqKiBGaW5kIHRoZSAnY2FyLmRhdGEuY3N2JyBmaWxlIGluIHRoZSAnZGF0YScgc3ViZm9sZGVyIHN1cHBsaWVkIHdpdGggTGFiIDEuIE9wZW4gdGhlIGZpbGUgd2l0aCBNaWNyb3NvZnQgRXhjZWwgKFBsZWFzZSBkbyBub3QgcGVyZm9ybSBhbnkgc3VnZ2VzdGVkIHNhdmluZyBvcHRpb25zIGJ5IE1TIEV4Y2VsKS4gTm90ZSBob3cgdGhlIGRhdGEgYW5kIGhlYWRlcnMgYXJlIGdpdmVuLiBDbG9zZSB0aGUgZmlsZS4gSWYgeW91IGFyZSBpbiBNYWMgZW52aXJvbm1lbnQsIGZvbGxvdyB0aGUgc3RlcHMgaW4gYSBzcHJlYWRzaGVldCBhcHBsaWNhdGlvbi4gDQoNCiogKipRMyoqIE9wZW4gdGhlICdjYXIuZGF0YS5jc3YnIGZpbGUgd2l0aCBNaWNyb3NvZnQgV29yZHBhZC4gRG8geW91IG5vdGljZSBhbnkgZGlmZmVyZW5jZSBpbiB0aGUgdmlldz8gDQo8IS0tIElmIHlvdSBhcmUgaW4gTWFjIHVzZSB0aGUgcmVsYXZhbnQgcHJvZ3JhbSAtLT4NCg0KKiAqKlE0KiogQ3JlYXRlIGEgZHVwbGljYXRlIG9mIHRoZSBmaWxlICdjYXIuZGF0YS5jc3YnIGFuZCBzYXZlIGl0IGFzICdtYW51YWxseWVkaXQuY2FyLmRhdGEuY3N2Jy4gT3BlbiB0aGlzIGZpbGUgY2hhbmdlIHRoZSBmaXJzdCBjb2x1bW4gbmFtZSBmcm9tICdidXlpbmcnIHRvICdCdXlpbmcnLiBTYXZlIHRoZSBjaGFuZ2VzIGFuZCBjbG9zZSB0aGUgZmlsZS4NCg0KKiAqKlE1KiogQ2F0ZWdvcml6ZSB0aGUgZGF0YSB0eXBlcyBpbiB0aGUgZmlsZSBhcyBxdWFudGl0YXRpdmUgb3IgcXVhbGl0aWF0aXZlLg0KDQojIyMgMS4xLjIgSW1wb3J0aW5nIGRhdGEgaW50byBSDQoNCiogKipRNioqIFVzZSB0aGUgZm9sbG93aW5nIGNvZGUgc25pcHBldCB0byBsb2FkIHRoZSBjb250ZW50IG9mIHRoZSAnY2FyLmRhdGEuY3N2JyBpbnRvIGFuIFIgdGFibGUgZGF0YSBzdHJ1Y3R1cmUuIEFsdGhvdWdoIHRoZSBVUkwgd29ya3MsIHlvdSBoYXZlIHRvIHByb3ZpZGUgdGhlIHBhdGggdG8gdGhlIGRhdGEgZmlsZSBpbiB5b3VyIGNvZGUuDQpgYGB7cn0NCnVjaUNhcj1yZWFkLnRhYmxlKGZpbGU9J2h0dHA6Ly93d3cud2luLXZlY3Rvci5jb20vZGZpbGVzL2Nhci5kYXRhLmNzdicsaGVhZGVyID0gVFJVRSxzZXAgPSAnLCcpDQpgYGANCg0KKiAqKlE3KiogSW1wb3J0IHRoZSBzYW1lIGRhdGFzZXQgaW50byBhIGRpZmZlcmVudCB2YXJpYWJsZSAndWNpQ2FyV3IndXNpbmcgdGhlIHJlYWQudGFibGUoKSBmdW5uY3Rpb24uIFNldCAnRkFMU0UnIHRvIHRoZSBoZWFkZXIgcGFyYW1ldGVyLg0KDQoNCiMjIyAxLjEuMyBFeGFtaW5pbmcgdGhlIGltcG9ydGVkIGRhdGENCg0KKiAqKlE4KiogVXNlICoqZGltKiogZnVuY3Rpb24gdG8gZ2V0IHRoZSBkaW1lbnNpb25zIG9mIHRoZSBkYXRhc2V0LiBIb3cgbWFueSBkYXRhIGVudHJpZXMgYXJlIHRoZXJlIGluIHRoZSBkYXRhc2V0Pw0KPCEtLSMgYGBge3J9DQojIGRpbSh1Y2lDYXIpDQojIGBgYC0tPg0KDQoqICoqUTkqKiBIb3cgbWFueSByb3dzIGFyZSB0aGVyZSBpbiB0aGUgJ3VjaUNhcldyJz8gV2h5IHRoZSBhbnN3ZXIgaXMgZGlmZmVyZW50IHRvIHRoZSBhbnN3ZXIgZnJvbSAqKlE3KioNCg0KKiAqKlExMCoqIFVzZSB0aGUgJ2NsYXNzKCknIGZ1bmN0aW9uIHRvIGZpbmQgdGhlIHR5cGUgb2YgdGhlIHZhcmlhYmxlcyBpbiAndWNpQ2FyJy4gRS5nLiBjbGFzcyh1Y2lDYXIkYnV5aW5nKSB3aWxsIHJldHVybiB0aGUgdHlwZSBvZiB0aGUgJ2J1eWluZycgdmFyaWFibGUuIEFyZSB0aGV5IGFsbCBkaWZmZXJlbnQgb3Igc2FtZT8gV3JpdGUgeW91ciBvYnNlcnZhdGlvbiBhcyBjb21tZW50DQoNCiogKipRMTEqKiBFbnRlciB0aGUgZm9sbG93aW5nIGNvbW1hbmQuIERlc2NyaWJlIHRoZSBtZWFuaW5nIG9mIGVudHJpZXMgdW5kZXIgJ2Rvb3JzJy4NCmBgYHtyfQ0Kc3VtbWFyeSh1Y2lDYXIpDQpgYGANCiMjIyAxLjEuNCBXb3JraW5nIHdpdGggb3RoZXIgZGF0YSBmb3JtYXRzDQpSIGhhcyBtYW55IGFkYXB0b3JzIHRoYXQgY2FuIGJlIHVzZWQgdG8gaW1wb3J0IGRhdGEgZnJvbSBhIHZhcmlldHkgb2Ygc291cmNlcy4gDQoNCiogWExTL1hMU1jigJRodHRwOi8vY3Jhbi5yLXByb2plY3Qub3JnL2RvYy9tYW51YWxzL1ItZGF0YS5odG1sI1JlYWRpbmctRXhjZWwtc3ByZWFkc2hlZXRzIA0KKu+CoSBKU09O4oCUaHR0cDovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcmpzb24vaW5kZXguaHRtbCANCirvgqEgWE1M4oCUaHR0cDovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvWE1ML2luZGV4Lmh0bWwgDQoq74KhIE1vbmdvRELigJRodHRwOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9ybW9uZ29kYi9pbmRleC5odG1sIA0KKu+CoSBTUUzigJRodHRwOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9EQkkvaW5kZXguaHRtbA0KDQoNCiMjIDEuMiBXb3JraW5nIHdpdGggTGVzcy1TdHJ1Y3R1cmVkIERhdGENCk5vdCBhbGwgZGF0YSBhcmUgc3RvcmVkIGluIGEgc3RydWN0dXJlZCBtYW5uZXIuIE9mdGVuIGRhdGEgc2NpZW50aXN0cyB1c2Ugc2NyaXB0cyB0byBtYW5pcHVsYXRlIGltcG9ydGVkIGRhdGEgdG8gaW1wb3NlIGEgc3RydWN0dXJlLCBzbyB0aGF0IHRoZSBkYXRhIGNhbiBiZSBhbmFseXNlZCBjb252ZW5pZW50bHkuIA0KDQoqICoqUTEyKiogR28gdG8gdGhlIFVSTCAnaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvZGF0YXNldHMvU3RhdGxvZysoR2VybWFuK0NyZWRpdCtEYXRhKScgd2hpY2ggaG9zdHMgdGhlIEdlcm1hbiBCYW5rIENyZWRpdCBEYXRhc2V0LiBEb3dubG9hZCB0aGUgZGF0YXNldCBpbnRvIHlvdXIgd29ya2luZyBkaXJlY3RvcnkncyBkYXRhIGZvbGRlci4gDQoNCiogKipRMTMqKiBVc2UgdGhlIGZvbGxvd2luZyBjb21tYW5kIHRvIGxvYWQgdGhlIGRhdGEgaW50byBhIHZhcmlhYmxlIGNhbGxlZCAnZCcuIFVzZSB0aGUgZG93bmxvYWRlZCBmaWxlIHBhdGggYW5kIHR5cGUgaW4geW91ciBjb2RlLiBXaGF0IGFyZSB0aGUgY29sdW1uIG5hbWVzPw0KYGBge3J9DQpkIDwtIHJlYWQudGFibGUocGFzdGUoJ2h0dHA6Ly9hcmNoaXZlLmljcy51Y2kuZWR1L21sLycsDQonbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMvc3RhdGxvZy9nZXJtYW4vZ2VybWFuLmRhdGEnLHNlcD0nJyksc3RyaW5nc0FzRmFjdG9ycz1GLGhlYWRlcj1GKQ0KYGBgDQoNCiogKipRMTQqKiBDaGFuZ2UgdGhlIGNvbHVtbiBuYW1lcyBvZiB0aGUgZGF0YXNldCB1c2luZyB0aGUgJ2NvbG5hbWVzKCknIGZ1bmN0aW9uIGFzIGZvbGxvd3MuIFRoZSAnYygpJyBjb21tYW5kIGNvbnN0cnVjdCBhIHZlY3RvciB0aGF0IGNvbnRhaW5zIHRoZSBkZXNpcmVkIGNvbHVtbiBuYW1lcy4NCmBgYHtyfQ0KY29sbmFtZXMoZCkgPC0gYygnU3RhdHVzLm9mLmV4aXN0aW5nLmNoZWNraW5nLmFjY291bnQnLA0KJ0R1cmF0aW9uLmluLm1vbnRoJywgJ0NyZWRpdC5oaXN0b3J5JywgJ1B1cnBvc2UnLA0KJ0NyZWRpdC5hbW91bnQnLCAnU2F2aW5ncyBhY2NvdW50L2JvbmRzJywNCidQcmVzZW50LmVtcGxveW1lbnQuc2luY2UnLCdJbnN0YWxsbWVudC5yYXRlLmluLnBlcmNlbnRhZ2Uub2YuZGlzcG9zYWJsZS5pbmNvbWUnLA0KJ1BlcnNvbmFsLnN0YXR1cy5hbmQuc2V4JywgJ090aGVyLmRlYnRvcnMvZ3VhcmFudG9ycycsDQonUHJlc2VudC5yZXNpZGVuY2Uuc2luY2UnLCAnUHJvcGVydHknLCAnQWdlLmluLnllYXJzJywNCidPdGhlci5pbnN0YWxsbWVudC5wbGFucycsICdIb3VzaW5nJywNCidOdW1iZXIub2YuZXhpc3RpbmcuY3JlZGl0cy5hdC50aGlzLmJhbmsnLCAnSm9iJywNCidOdW1iZXIub2YucGVvcGxlLmJlaW5nLmxpYWJsZS50by5wcm92aWRlLm1haW50ZW5hbmNlLmZvcicsDQonVGVsZXBob25lJywgJ2ZvcmVpZ24ud29ya2VyJywgJ0dvb2QuTG9hbicpDQpgYGANCg0KVXNpbmcgYSAqKmRhdGEgZGljdGlvbmFyeSoqIG9yIGEgKipzY2hlbWEgZG9jdW1lbnRhdGlvbioqIHlvdSBjYW4gdHJhbnNmb3JtIGRhdGEgaW4gUi4gQSBsaXN0IGluIFIgY2FuIGJlIHVzZWQgdG8gY3JlYXRlIHN1Y2ggYSBtYXBwaW5nIHN0cnVjdHVyZS4gRm9yIGV4YW1wbGUsIG1lYW5pbmcgb2YgdGhlIEEtKiBjb2RlcyBjYW4gYmUgZGVjb2RlZCB1c2luZyBhIGxpc3QgbGlrZSBiZWxvdy4NCg0KYGBge3J9DQptYXBwaW5nIDwtIGxpc3QoDQonQTQwJz0nY2FyIChuZXcpJywNCidBNDEnPSdjYXIgKHVzZWQpJywNCidBNDInPSdmdXJuaXR1cmUvZXF1aXBtZW50JywNCidBNDMnPSdyYWRpby90ZWxldmlzaW9uJywNCidBNDQnPSdkb21lc3RpYyBhcHBsaWFuY2VzJw0KKQ0KYGBgDQoNClRoZScgYWN0dWFsIGRhdGEgY2FuIGJlIGRlY29kZWQgdXNpbmcgdGhlICdtYXBwaW5nJyBsaXN0IGFzIGZvbGxvd3MuDQoNCmBgYHtyfQ0KZm9yKGkgaW4gMTooZGltKGQpKVsyXSkgew0KICAgIGlmKGNsYXNzKGRbLGldKT09J2NoYXJhY3RlcicpIHsNCiAgICAgICAgZFssaV0gPC0gYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihtYXBwaW5nW2RbLGldXSkpDQogICAgfQ0KfQ0KYGBgDQoqICoqUTE1KiogVXNlIHRoZSBhYm92ZSBjb2RlIHRvIHRyYW5zZm9ybSB0aGUgZGF0YS4gVGhlbiwgdXNlIHRoZSBmb2xsb3dpbmcgY29kZSB0byB2aWV3IHRoZSBkYXRhDQoNCmBgYHtyfQ0KdGFibGUoZCRQdXJwb3NlLGQkR29vZC5Mb2FuKQ0KYGBgDQoNCg0KIyAyLiBXb3JraW5nIHdpdGggUmVsYXRpb25hbCBEYXRhYmFzZXMNCg0KUmVsYXRpb25hbCBkYXRhYmFzZXMgY2FuIHNjYWxlIHVwdG8gbWlsbGlvbnMgb2YgcmVjb3Jkcy4gQW5kIHN0cnVjdHVyZWQgcXVlcmllcyBjYW4gYmUgZmV0ZWNoZWQgZWFzaWx5IHdpdGggcmVsYXRpb25hbCBkYXRhYmFzZXMuIEluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbnMsIHdlIHdpbGwgZXhhbWluZSwgaG93IHRvIGdldCBkYXRhIGZyb20gYSBkYXRhYmFzZSBpbnRvIFIgZm9yIGFuYWx5c2lzLg0KDQojIyAyLjEgQ3VyYXRpbmcgdGhlIERhdGENCllvdSBuZWVkIHRvIHJlY29yZCB0aGUgc291cmNlIG9mIHRoZSBkYXRhIHByb3Blcmx5LiBHbyB0byB0aGUgYmVyZWF1IG9mIG1ldGVvcm9sb2d5IGFuZCBnZXQgZG93bmxvYWQgY2xpbWF0ZSBkYXRhIGZvciBqdWx5IDIwMTYgYW5kIGp1bHkgMjAxNyBtb250aHMuDQoNCg0KKiAqKlExNioqIENyZWF0ZSBhIGRhdGEgcHJvdm9uZW5jZSBkb2N1bWVudGF0aW9uIGZvciB0aGlzIGRhdGFzZXQuIFlvdXIgbm90ZSBzaG91bGQgaW5jbHVkZSBkYXRhIGFjY2VzcyBkYXRlLCBzb3VyY2UgdXJsLCBob3cgeW91IG5hdmlnYXRlIHRoZSBkYXRhIGluIHRoZSBnaXZlbiB1cmwsIGRhdGEgZm9ybWF0IGFuZCBjcnlwdG9ncmFwaGljIGhhc2hlcy4gKCBIaW50OiBZb3UgY2FuIGZpbmQgYSBzaW1pbGFyIGRvY3VtZW50IGluIGNoYXB0ZXIgMiBvZiB0aGUgcmVmZXJlbmNlIGJvb2spDQoNCiMjIDIuMyBMb2FkaW5nIERhdGEgZnJvbSBhIERhdGFiYXNlIGludG8gUg0KDQoqICoqUTE3KiogRXhwbG9yZSB0aGUgJ2p1bHljbGltYXRlLmRiJyBkYXRhYmFzZSAgaW4gdGhlICdkYXRhL2RiJyBmb2xkZXIgdXNpbmcgYW4gU1FMIGRhdGFiYXNlIHZpZXdlci4gSG93IG1hbnkgcmVjb3JkcyBhcmUgdGhlcmUgaW4gdGhlIGRhdGFzZXQ/DQoNCiogKipRMTgqKiBVc2luZyB0aGUgZm9sbG93aW5nIGNvZGUsIGltcG9ydCB0aGUgZGF0YSBpbiB0aGUgZGF0YWJhc2UgaW50byBSLg0KDQpgYGB7cn0NCnJlcXVpcmUoREJJKQ0KcmVxdWlyZShSU1FMaXRlKQ0KDQpEQkZJTEVOQU1FPC0nQzpcXGxhYjFcXGRhdGFcXGRiXFxqdWx5Y2xpbWF0ZS5kYicNCg0KdGhpc2RiIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgREJGSUxFTkFNRSkNCnN0YXJ0X2RhdGUgPC0nMDEtMDctMjAxNicNCmVuZF9kYXRlICAgPC0nMzEtMDctMjAxNicNCiNxdWVyeSA9IHNwcmludGYoIlNFTEVDVCAqIEZST00ganVseTE2IFdIRVJFIERhdGUgQkVUV0VFTiAnJXMnIEFORCAnJXMnIixzdGFydF9kYXRlLGVuZF9kYXRlKQ0KcXVlcnk9ICdTRUxFQ1QgKiBGUk9NIGp1bHkxNicNCnJhd2RhdGE8LWRiR2V0UXVlcnkodGhpc2RiLHF1ZXJ5KQ0KZGJEaXNjb25uZWN0KHRoaXNkYikNCmBgYA0KDQoqICoqUTE5KiogT3BlbiB0aGUgZGF0YWJhc2UgaW4geW91ciB3b3JraW5nIGZvbGRlcnMgKCdkYXRhXGRiXGp1bHljbGltYXRlLmRiJykgaW4gdGhlIGRhdGFiYXNlIHZpZXdlci4gRmluZCB0aGUgb3RoZXIgdGFibGVzLiBDaGFuZ2UgdGhlIGFib3ZlIGNvZGUgdG8gaW1wb3J0IHRoZSBqdWx5IDIwMTcgZGF0YSBpbnRvIFIgYW5kIHNhdmUgaXQgaW4gJ2p1bHkxNycgdmFyaWFibGUuIEFsc28gY2hhbmdlIHRoZSB2YXJpYWJsZSBuYW1lICdyYXdkYXRhJyBhcyAnanVseTE2JyANCg0KYGBge3J9DQpqdWx5MTY8LXJhd2RhdGENCmBgYA0KDQoNCiogKipRMjAqKiBZb3UgY2FuIHNlbGVjdCBhIHN1YnNldCBvZiBkYXRhIGJ5IG1hbmlwdWxhdGluZyB0aGUgZGF0YSBmcmFtZXMgKHRoaXMgaXMgdGhlIGRhdGEgc3RydWN0dXJlIG5hbWUpLiBFeGVjdXRlIHRoZSBmb2xsb3dpbmcgY29kZSB0byBmaW5kIHRoZSBkYXlzIHdpdGggbWluaW11bSB0ZW1wZXJhdHVyZXMNCg0KDQpgYGB7cn0NCm1pbnRlbXBfanVsMTY8LWhlYWQoc29ydChqdWx5MTYkYE1pbmltdW10ZW1wZXJhdHVyZSjvv71DKWApKQ0KbWludGVtcF9qdWwxNg0KYGBgDQoNCg0KKiAqKlEyMSoqIEZpbmQgdGhlIGxvd2VzdCAobWluaW11bSA1KSB0ZW1wZXJhdHVyZXMgaW4ganVseSAyMDE3IGRhdGEuIFNhdmUgdGhlIHJlc3VsdHMgaW4gbWludGVtcF9qdWx5MTcgdmFyaWFibGUuIEFyZSB0aGVyZSBhbnkgdmFyaWF0aW9ucyBpbiB0aGUgbWluaW11bSB0ZW1wZXJhdHVyZXM/DQoNCg0KKiAqKlEyMioqDQpZb3UgY2FuIGdldCBhIHF1aWNrIG92ZXJ2aWV3IG9mIGEgZGF0YXNldCB1c2luZyB0aGUgJ3N1bW1hcnknIGZ1bmN0aW9uLiBFeHBsb3JlIGluZm9ybWF0aW9uIGdpdmVuIGJ5IHRoZSBzdW1tYXJ5IGZ1bmN0aW9uLg0KDQpgYGB7cn0NCiBzdW1tYXJ5KGp1bHkxNikNCmBgYA0KDQo8IS0tICogKipRMjMqKiAgUnVuIHRoZSBmb2xsb3dpbmcgY29kZSB0byB2aXN1YWxpc2UgdGhlIGRhdGEgaW4gMjAxNi4gU2ltaWxhcmx5LCBnZW5lcmF0ZSBhIGdyYXBoIGZvciAyMDE3IGp1bHkgcmFpbmZhbGwgZGF0YS4gIC0tPg0KDQoNCjwhLS0gYGBge3J9IC0tPg0KPCEtLSBwYXIobGFzPTIpIC0tPg0KPCEtLSBwYXIobWFyPWMoOCw4LDEsMSkpIC0tPg0KPCEtLSBwbG90KGp1bHkxNiRgUmFpbmZhbGwobW0pYCx0eXBlID0gJ2InLGNvbD0ncmVkJyxtYWluPSdKdWx5IDIwMTYgUmFpbmZhbGwnLHhheHQ9TlVMTCx5bGFiPSdSYWluZmFsbCAobW0pJyx4bGFiPScnKSAtLT4NCjwhLS0gYXhpcyhzaWRlID0gMSxhdCA9IDE6bGVuZ3RoKGp1bHkxNiRgUmFpbmZhbGwobW0pYCksbGFiZWxzID0ganVseTE2JERhdGUsKSAtLT4NCjwhLS0gYGBgIC0tPg0KDQoNCg0K