【目錄】

1. Data Import
2. Tidy Data
3. Web Scraping
4. String Processing


【Data Import】

system.file() #回傳根目錄"C:/PROGRA~1/R/R-35~1.0/library/base"
file.path(路徑,檔名) #會幫你自動選對的slash黏上 
file.copy(被複製的路徑,欲複製到的路徑)


【R的基本function VS readr ?】

  • Base R functions import data as a data frame, while “readr” functions import data as a tibble.
  • 如果read.csv時沒有用StringAsFactor=F,他會將character轉為factor
  • The base R import functions can read .csv files,but cannot files with other delimiters, such as .tsv files, or fixed-width files.
  • The base R import functions can read files with other delimiters(分隔號) like .tsv using read.delim and can read fixed-width files using read.fwf.


【What is Tibble ?】

  • A tibble is also a data.frame.
    • Tibble prints better in console (it fits to screen size) and it also prints data types in header.
    • To put it simple, tibbles are “evolutions” of data.frames, printing and subsetting are less troublesome with tibbles than data.frames.
    • For example, if you want to look 10 first rows of a data.frame, you will use head(data.frame), for tibbles you can simple use print(tibble) or call tibble in R Console.
  • When subsetting, if you use “[” on tibble R will return always another tibble, but when you use “[[” R will return a vector.
  • For data.frames, when using “[” you might get a vector or another data.frame, which is quite unpleasant.
  • Another difference, tibble does not have partial matching.
url = https://~/murders.csv
read_csv(url)                       # 可以直接讀取網址檔
download.file(url, "murders.csv")   # 把網上的檔案載下來並命名為murders.csv
tempdir() 
tempfile()
# tempfile創建一個字符串,而不是文件,這可能是一個唯一的文件名


【Tidy Data】

dat_tidy <- dat_wide %>%
gather(key = disease, value = count, “Hepatitis A”: “Rubella”)
gather(key = key, value = value, -age_group)  # 把除了其中一個以外的全部的變數都拿去gather
D %>% gather(key=key, value=value, -age_group) %>%
  separate(col=key, into=c("year", "variable_name"), "_") %>% 
  spread(key=variable_name, value=value) %>% data.frame
# 默認的separate就是"_"了,所以即使不寫也沒關係
stats %>%
  separate(col=key, into=c("player", "variable_name"), sep="_", extra="merge") %>% 
  spread(key=variable_name, value=value)
# 等同於
stats %>%
  separate(col=key, into=c("player", "variable_name1", "variable_name2"), 
           sep="_", fill="right") %>% 
  unite(col = variable_name, variable_name1, variable_name2, sep = "_") %>% 
  spread(key = variable_name, value = value)
# fill="right"是叫他把name1,name2的欄位黏在player右邊


【Merge data】

  • left_join()
    • all rows in the left-hand table are retained in the final table, columns from both tables will be included in the final table
    • 左邊保持原樣,右邊對照左邊的obs.把右邊的column和值加入(NA來自右邊的)
  • right_join()
    • 與left_join相反
  • inner_join()
    • 選兩邊都有obs.的column,把他們merge起來(取交集)
  • full_join()
    • 不管是否欄列對稱,全部資料保留,缺項塞NA(取聯集)
  • semi_join()
    • 從inner_join的结果中只取左邊的值
  • anti_join()
    • 與semi_join相反,為左邊獨有的obs.


Bind_column(tb1,tb2) == cbind()
Bind_row(tb1,tb2,tb3) == rbind()
  • Binding functions combine by position, while join functions match by variables.

  • Joining functions can join datasets of different dimensions, but the bind functions must match on the appropriate dimension (either same row or column numbers).

  • Bind functions can combine both vectors and dataframes, while join functions work for only for dataframes.

intersect(c(1:10),c(6:15))
intersect(c("a","b"),c("b","c")) 
setdiff(c(1:10),c(6:15))
[1] 1 2 3 4 5
setequal(c(1:10),c(6:15))
[1] FALSE
setequal(c(1:10),c(1:10))
[1] TRUE
union(c(1:10),c(6:15))
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15


【Web Scraping】


library(rvest)
url = https://~
h = read_html(url)
tab = h %>% html_nodes("table")
tab = tab[[2]] # 還是一行一行亂亂的
tab = tab %>% html_table
class(tab)
[1]  "data.frame"

For the guacamole recipe page we already have done this and determined that we need the following selectors:

library(rvest)
h <- read_html("http://www.foodnetwork.com/recipes/alton-brown/guacamole-recipe-1940609")
recipe <- h %>% html_node(".o-AssetTitle__a-HeadlineText") %>% html_text()
prep_time <- h %>% html_node(".o-RecipeInfo__a-Description--Total") %>% html_text()
ingredients <- h %>% html_nodes(".o-Ingredients__a-ListItemText") %>% html_text()

You can see how complex the selectors are. In any case we are now ready to extract what we want and create a list:

guacamole <- list(recipe, prep_time, ingredients)

Since recipe pages from this website follow this general layout, we can use this code to create a function that extracts this information:

get_recipe <- function(url){
    h <- read_html(url)
    recipe <- h %>% html_node(".o-AssetTitle__a-HeadlineText") %>% html_text()
    prep_time <- h %>% html_node(".o-RecipeInfo__a-Description--Total") %>% html_text()
    ingredients <- h %>% html_nodes(".o-Ingredients__a-ListItemText") %>% html_text()
    return(list(recipe = recipe, prep_time = prep_time, ingredients = ingredients))
}

get_recipe("http://www.foodnetwork.com/recipes/food-network-kitchen/pancakes-recipe-1913844")

Below is an example for Scraping rain data from a website:

year <- c(2013:2017)
date <- c(201301:201312,201401:201412,201501:201512,201601:201612,201701:201712)
td <- data.frame('rd'=NA)
url <- paste0('http://e-service.cwb.gov.tw/HistoryDataQuery/YearDataController.do?command=viewMain&station=466910&stname=%25E9%259E%258D%25E9%2583%25A8&datepicker=',year)
for( i in 1:5){
  rd <- url[i] %>% 
    GET() %>% 
    read_html() %>% 
    html_nodes(.,css='td:nth-child(21)') %>% 
    html_text()
  rd <- data.frame('rd'=rd)
  td <- rbind(td,rd)
}
td <- td[-1,]
d1 <- data.frame('date'=date)
d1 <- cbind(d1,td)

d1$td <- gsub("\U00A0", "", d1$td)
View(d1)

write.csv(d1, file="C:/Users/shiki/Downloads/1.xlsx")


【String Processing】

str_detect(tab$population, ",")   # 回傳TRUE/FALSE
str_subset(tab$population, ",")   # 回傳字串 ex. 123,456/1,000
str_extract(tab$population, ",")  # 回傳逗號(提取""裡面的東西)











LS0tDQp0aXRsZTogIkRhdGEgV2FyZ2xpbmcgUiBOb3RlYm9vayINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyDjgJDnm67pjITjgJENCg0KWzEuIERhdGEgSW1wb3J0XSgjbjEpIDxicj4NClsyLiBUaWR5IERhdGFdKCNuMikgPGJyPg0KWzMuIFdlYiBTY3JhcGluZ10oI24zKSA8YnI+DQpbNC4gU3RyaW5nIFByb2Nlc3NpbmddKCNuNCkgPGJyPg0KDQo8aHI+DQoNCiMjIyA8YSBpZD0ibjEiPjwvYT7jgJBEYXRhIEltcG9ydOOAkQ0KDQpgYGB7cn0NCnN5c3RlbS5maWxlKCkgI+WbnuWCs+agueebrumMhCJDOi9QUk9HUkF+MS9SL1ItMzV+MS4wL2xpYnJhcnkvYmFzZSINCmZpbGUucGF0aCjot6/lvpEs5qqU5ZCNKSAj5pyD5bmr5L2g6Ieq5YuV6YG45bCN55qEc2xhc2jpu4/kuIogDQpmaWxlLmNvcHko6KKr6KSH6KO955qE6Lev5b6RLOassuikh+ijveWIsOeahOi3r+W+kSkNCmBgYA0KDQorIHJlYWRfbGluZXMoKSAj5p+l55yL5pyJ5rKS5pyJaGVhZGVyDQorIFRoZSBpbXBvcnQgZnVuY3Rpb25zIGluIHRoZSByZWFkciBwYWNrYWdlIGFsbCBzdGFydCBhcyByZWFkXywgd2hpbGUgdGhlIGltcG9ydCBmdW5jdGlvbnMgZm9yIGJhc2UgUiBhbGwgc3RhcnQgd2l0aCByZWFkLg0KDQo8YnI+DQoNCiMjIyMg44CQUueahOWfuuacrGZ1bmN0aW9uIFZTIHJlYWRyID/jgJENCg0KKyBCYXNlIFIgZnVuY3Rpb25zIGltcG9ydCBkYXRhIGFzIGEgZGF0YSBmcmFtZSwgd2hpbGUgInJlYWRyIiBmdW5jdGlvbnMgaW1wb3J0IGRhdGEgYXMgYSB0aWJibGUuDQorIOWmguaenHJlYWQuY3N25pmC5rKS5pyJ55SoU3RyaW5nQXNGYWN0b3I9Ru+8jOS7luacg+Wwh2NoYXJhY3Rlcui9ieeCumZhY3Rvcg0KKyBUaGUgYmFzZSBSIGltcG9ydCBmdW5jdGlvbnMgY2FuIHJlYWQgLmNzdiBmaWxlcyxidXQgY2Fubm90IGZpbGVzIHdpdGggb3RoZXIgZGVsaW1pdGVycywgc3VjaCBhcyAudHN2IGZpbGVzLCBvciBmaXhlZC13aWR0aCBmaWxlcy4NCisgVGhlIGJhc2UgUiBpbXBvcnQgZnVuY3Rpb25zIGNhbiByZWFkIGZpbGVzIHdpdGggb3RoZXIgZGVsaW1pdGVycyjliIbpmpTomZ8pIA0KbGlrZSAudHN2IHVzaW5nIHJlYWQuZGVsaW0gYW5kIGNhbiByZWFkIGZpeGVkLXdpZHRoIGZpbGVzIHVzaW5nIHJlYWQuZndmLg0KDQo8YnI+DQoNCiMjIyMg44CQV2hhdCBpcyBUaWJibGUgP+OAkQ0KDQorIEEgdGliYmxlIGlzIGFsc28gYSBkYXRhLmZyYW1lLiANCiAgICArIFRpYmJsZSBwcmludHMgYmV0dGVyIGluIGNvbnNvbGUgKGl0IGZpdHMgdG8gc2NyZWVuIHNpemUpIGFuZCBpdCBhbHNvIHByaW50cyBkYXRhIHR5cGVzIGluIGhlYWRlci4NCiAgICArIFRvIHB1dCBpdCBzaW1wbGUsIHRpYmJsZXMgYXJlICJldm9sdXRpb25zIiBvZiBkYXRhLmZyYW1lcywgcHJpbnRpbmcgYW5kIHN1YnNldHRpbmcgYXJlIGxlc3MgdHJvdWJsZXNvbWUgd2l0aCB0aWJibGVzIHRoYW4gZGF0YS5mcmFtZXMuDQogICAgKyBGb3IgZXhhbXBsZSwgaWYgeW91IHdhbnQgdG8gbG9vayAxMCBmaXJzdCByb3dzIG9mIGEgZGF0YS5mcmFtZSwgeW91IHdpbGwgdXNlIGhlYWQoZGF0YS5mcmFtZSksIGZvciB0aWJibGVzIHlvdSBjYW4gc2ltcGxlIHVzZSBwcmludCh0aWJibGUpIG9yIGNhbGwgdGliYmxlIGluIFIgQ29uc29sZS4NCisgV2hlbiBzdWJzZXR0aW5nLCBpZiB5b3UgdXNlICJbIiBvbiB0aWJibGUgUiB3aWxsIHJldHVybiBhbHdheXMgYW5vdGhlciB0aWJibGUsIGJ1dCB3aGVuIHlvdSB1c2UgIltbIiBSIHdpbGwgcmV0dXJuIGEgdmVjdG9yLg0KKyBGb3IgZGF0YS5mcmFtZXMsIHdoZW4gdXNpbmcgIlsiIHlvdSBtaWdodCBnZXQgYSB2ZWN0b3Igb3IgYW5vdGhlciBkYXRhLmZyYW1lLCB3aGljaCBpcyBxdWl0ZSB1bnBsZWFzYW50LiANCisgQW5vdGhlciBkaWZmZXJlbmNlLCB0aWJibGUgZG9lcyBub3QgaGF2ZSBwYXJ0aWFsIG1hdGNoaW5nLg0KDQpgYGB7cn0NCnVybCA9IGh0dHBzOi8vfi9tdXJkZXJzLmNzdg0KcmVhZF9jc3YodXJsKSAgICAgICAgICAgICAgICAgICAgICAgIyDlj6/ku6Xnm7TmjqXoroDlj5bntrLlnYDmqpQNCmRvd25sb2FkLmZpbGUodXJsLCAibXVyZGVycy5jc3YiKSAgICMg5oqK57ay5LiK55qE5qqU5qGI6LyJ5LiL5L6G5Lim5ZG95ZCN54K6bXVyZGVycy5jc3YNCnRlbXBkaXIoKSANCnRlbXBmaWxlKCkNCiMgdGVtcGZpbGXlibXlu7rkuIDlgIvlrZfnrKbkuLLvvIzogIzkuI3mmK/mlofku7bvvIzpgJnlj6/og73mmK/kuIDlgIvllK/kuIDnmoTmlofku7blkI0NCmBgYA0KDQo8YnI+DQo8aHI+DQoNCiMjIyA8YSBpZD0ibjIiPjwvYT7jgJBUaWR5IERhdGHjgJENCg0KYGBge3J9DQpkYXRfdGlkeSA8LSBkYXRfd2lkZSAlPiUNCmdhdGhlcihrZXkgPSBkaXNlYXNlLCB2YWx1ZSA9IGNvdW50LCDigJxIZXBhdGl0aXMgQeKAnTog4oCcUnViZWxsYeKAnSkNCmBgYA0KICANCisgc3BlY2lmaWVkIHRoYXQgdGhlIOKAnGtleeKAnSBjb2x1bW4gd2lsbCBiZSBjYWxsZWQg4oCcZGlzZWFzZeKAnSwgdGhlIHZhbHVlIG9mIGVhY2ggZW50cnkgd2lsbCBiZSBjYWxsZWQg4oCcY291bnTigJ0sIGFuZCB0aGF0IHRoZSBjb2x1bW5zIEhlcGF0aXRpcyBBIHRocm91Z2ggUnViZWxsYSB3aWxsIGFsbCBiZSBpbmNsdWRlZCBpbiB0aGUgZ2F0aGVyIGNvbW1hbmQuDQorIHNwZWFyZOi3n2dhdGhlcuebuOWPjSxzcGVhcmTmmK/miorkuIDlgIvmrITkvY3oo6HpnaJu5YCLZmFjdG9y6K6K5oiQbuWAi+ashOS9jSzkuKbmiormr49yb3flj7PpgornmoTlgLzovLjlhaVu5YCL5qyE5L2N5LiL6Z2iDQorIGdhdGhlcuaYr+aKim7lgIvmrITkvY3ogZrpm4bmiJAx5YCL5paw55qE5qyE5L2NLOS4puWJteesrDLlgIvmlrDnmoTmrITkvY3miopu5qyE5LiL55qEb2JzLui8uOWFpeWwjeaHieeahHJvdw0KDQpgYGB7cn0NCmdhdGhlcihrZXkgPSBrZXksIHZhbHVlID0gdmFsdWUsIC1hZ2VfZ3JvdXApICAjIOaKiumZpOS6huWFtuS4reS4gOWAi+S7peWklueahOWFqOmDqOeahOiuiuaVuOmDveaLv+WOu2dhdGhlcg0KYGBgDQoNCg0KYGBge3J9DQpEICU+JSBnYXRoZXIoa2V5PWtleSwgdmFsdWU9dmFsdWUsIC1hZ2VfZ3JvdXApICU+JQ0KICBzZXBhcmF0ZShjb2w9a2V5LCBpbnRvPWMoInllYXIiLCAidmFyaWFibGVfbmFtZSIpLCAiXyIpICU+JSANCiAgc3ByZWFkKGtleT12YXJpYWJsZV9uYW1lLCB2YWx1ZT12YWx1ZSkgJT4lIGRhdGEuZnJhbWUNCiMg6buY6KqN55qEc2VwYXJhdGXlsLHmmK8iXyLkuoYs5omA5Lul5Y2z5L2/5LiN5a+r5Lmf5rKS6Zec5L+CDQpgYGANCg0KDQpgYGB7cn0NCnN0YXRzICU+JQ0KICBzZXBhcmF0ZShjb2w9a2V5LCBpbnRvPWMoInBsYXllciIsICJ2YXJpYWJsZV9uYW1lIiksIHNlcD0iXyIsIGV4dHJhPSJtZXJnZSIpICU+JSANCiAgc3ByZWFkKGtleT12YXJpYWJsZV9uYW1lLCB2YWx1ZT12YWx1ZSkNCiMg562J5ZCM5pa8DQpzdGF0cyAlPiUNCiAgc2VwYXJhdGUoY29sPWtleSwgaW50bz1jKCJwbGF5ZXIiLCAidmFyaWFibGVfbmFtZTEiLCAidmFyaWFibGVfbmFtZTIiKSwgDQogICAgICAgICAgIHNlcD0iXyIsIGZpbGw9InJpZ2h0IikgJT4lIA0KICB1bml0ZShjb2wgPSB2YXJpYWJsZV9uYW1lLCB2YXJpYWJsZV9uYW1lMSwgdmFyaWFibGVfbmFtZTIsIHNlcCA9ICJfIikgJT4lIA0KICBzcHJlYWQoa2V5ID0gdmFyaWFibGVfbmFtZSwgdmFsdWUgPSB2YWx1ZSkNCiMgZmlsbD0icmlnaHQi5piv5Y+r5LuW5oqKbmFtZTEsbmFtZTLnmoTmrITkvY3pu4/lnKhwbGF5ZXLlj7PpgooNCmBgYA0KDQo8YnI+DQoNCiMjIyMg44CQTWVyZ2UgZGF0YeOAkQ0KDQorIGxlZnRfam9pbigpDQogICAgKyBhbGwgcm93cyBpbiB0aGUgbGVmdC1oYW5kIHRhYmxlIGFyZSByZXRhaW5lZCBpbiB0aGUgZmluYWwgdGFibGUsIGNvbHVtbnMgZnJvbSBib3RoIHRhYmxlcyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBmaW5hbCB0YWJsZQ0KICAgICsg5bem6YKK5L+d5oyB5Y6f5qijLOWPs+mCiuWwjeeFp+W3pumCiueahG9icy7miorlj7PpgornmoRjb2x1bW7lkozlgLzliqDlhaUoTkHkvoboh6rlj7PpgornmoQpDQorIHJpZ2h0X2pvaW4oKQ0KICAgICsg6IiHbGVmdF9qb2lu55u45Y+NDQorIGlubmVyX2pvaW4oKQ0KICAgICsg6YG45YWp6YKK6YO95pyJb2JzLueahGNvbHVtbizmiorku5blgJFtZXJnZei1t+S+hijlj5bkuqTpm4YpDQorIGZ1bGxfam9pbigpDQogICAgKyDkuI3nrqHmmK/lkKbmrITliJflsI3nqLEs5YWo6YOo6LOH5paZ5L+d55WZLOe8uumgheWhnk5BKOWPluiBr+mbhikNCisgc2VtaV9qb2luKCkNCiAgICArIOW+nmlubmVyX2pvaW7nmoTnu5PmnpzkuK3lj6rlj5blt6bpgornmoTlgLwNCisgYW50aV9qb2luKCkNCiAgICArIOiIh3NlbWlfam9pbuebuOWPjSzngrrlt6bpgornjajmnInnmoRvYnMuDQoNCjxicj4NCg0KYGBge3J9DQpCaW5kX2NvbHVtbih0YjEsdGIyKSA9PSBjYmluZCgpDQpCaW5kX3Jvdyh0YjEsdGIyLHRiMykgPT0gcmJpbmQoKQ0KYGBgDQoNCisgQmluZGluZyBmdW5jdGlvbnMgY29tYmluZSBieSBwb3NpdGlvbiwgd2hpbGUgam9pbiBmdW5jdGlvbnMgbWF0Y2ggYnkgdmFyaWFibGVzLg0KDQorIEpvaW5pbmcgZnVuY3Rpb25zIGNhbiBqb2luIGRhdGFzZXRzIG9mIGRpZmZlcmVudCBkaW1lbnNpb25zLCBidXQgdGhlIGJpbmQgZnVuY3Rpb25zIG11c3QgbWF0Y2ggb24gdGhlIGFwcHJvcHJpYXRlIGRpbWVuc2lvbiAoZWl0aGVyIHNhbWUgcm93IG9yIGNvbHVtbiBudW1iZXJzKS4NCg0KKyBCaW5kIGZ1bmN0aW9ucyBjYW4gY29tYmluZSBib3RoIHZlY3RvcnMgYW5kIGRhdGFmcmFtZXMsIHdoaWxlIGpvaW4gZnVuY3Rpb25zIHdvcmsgZm9yIG9ubHkgZm9yIGRhdGFmcmFtZXMuDQoNCg0KYGBge3J9DQppbnRlcnNlY3QoYygxOjEwKSxjKDY6MTUpKQ0KaW50ZXJzZWN0KGMoImEiLCJiIiksYygiYiIsImMiKSkgDQpgYGANCiANCmBgYHtyfQ0Kc2V0ZGlmZihjKDE6MTApLGMoNjoxNSkpDQpgYGANCg0KYGBge3J9DQpzZXRlcXVhbChjKDE6MTApLGMoNjoxNSkpDQpzZXRlcXVhbChjKDE6MTApLGMoMToxMCkpDQpgYGANCg0KYGBge3J9DQp1bmlvbihjKDE6MTApLGMoNjoxNSkpDQpgYGANCg0KPGJyPg0KPGhyPg0KDQojIyMgPGEgaWQ9Im4zIj48L2E+44CQV2ViIFNjcmFwaW5n44CRDQoNCjxicj4NCg0KKyBUaGUgaHRtbF9ub2RlIGNvbW1hbmQgb25seSBzZWxlY3RzIHRoZSBmaXJzdCBub2RlIG9mIGEgc3BlY2lmaWVkIHR5cGUuDQorIGh0bWxfbm9kZXMgc28gdGhhdCB3ZSBjb3VsZCBzcGVjaWZ5IHRoZSBlbGVtZW50cyB3ZSB3YW50LiANCiAgICArIEZvciBleGFtcGxlLCB0aGUgc2Vjb25kIOKAnHRhYmxl4oCdIGVsZW1lbnQgdXNpbmcgdGhlIHRhYltbMl1dIGNvbW1hbmQuDQorIHNlbGVjdG9yIGdhcmdldCDmlZnlrbg6aHR0cHM6Ly9zZWxlY3RvcmdhZGdldC5jb20vDQoNCmBgYHtyfQ0KbGlicmFyeShydmVzdCkNCnVybCA9IGh0dHBzOi8vfg0KaCA9IHJlYWRfaHRtbCh1cmwpDQp0YWIgPSBoICU+JSBodG1sX25vZGVzKCJ0YWJsZSIpDQp0YWIgPSB0YWJbWzJdXSAjIOmChOaYr+S4gOihjOS4gOihjOS6guS6gueahA0KdGFiID0gdGFiICU+JSBodG1sX3RhYmxlDQpjbGFzcyh0YWIpDQpbMV0gICJkYXRhLmZyYW1lIg0KYGBgDQogIA0KICANCkZvciB0aGUgZ3VhY2Ftb2xlIHJlY2lwZSBwYWdlIHdlIGFscmVhZHkgaGF2ZSBkb25lIHRoaXMgYW5kIGRldGVybWluZWQgdGhhdCB3ZSBuZWVkIHRoZSBmb2xsb3dpbmcgc2VsZWN0b3JzOg0KICANCmBgYHtyfQ0KbGlicmFyeShydmVzdCkNCmggPC0gcmVhZF9odG1sKCJodHRwOi8vd3d3LmZvb2RuZXR3b3JrLmNvbS9yZWNpcGVzL2FsdG9uLWJyb3duL2d1YWNhbW9sZS1yZWNpcGUtMTk0MDYwOSIpDQpyZWNpcGUgPC0gaCAlPiUgaHRtbF9ub2RlKCIuby1Bc3NldFRpdGxlX19hLUhlYWRsaW5lVGV4dCIpICU+JSBodG1sX3RleHQoKQ0KcHJlcF90aW1lIDwtIGggJT4lIGh0bWxfbm9kZSgiLm8tUmVjaXBlSW5mb19fYS1EZXNjcmlwdGlvbi0tVG90YWwiKSAlPiUgaHRtbF90ZXh0KCkNCmluZ3JlZGllbnRzIDwtIGggJT4lIGh0bWxfbm9kZXMoIi5vLUluZ3JlZGllbnRzX19hLUxpc3RJdGVtVGV4dCIpICU+JSBodG1sX3RleHQoKQ0KYGBgDQoNCllvdSBjYW4gc2VlIGhvdyBjb21wbGV4IHRoZSBzZWxlY3RvcnMgYXJlLiBJbiBhbnkgY2FzZSB3ZSBhcmUgbm93IHJlYWR5IHRvIGV4dHJhY3Qgd2hhdCB3ZSB3YW50IGFuZCBjcmVhdGUgYSBsaXN0Og0KICANCmBgYHtyfQ0KZ3VhY2Ftb2xlIDwtIGxpc3QocmVjaXBlLCBwcmVwX3RpbWUsIGluZ3JlZGllbnRzKQ0KYGBgDQoNClNpbmNlIHJlY2lwZSBwYWdlcyBmcm9tIHRoaXMgd2Vic2l0ZSBmb2xsb3cgdGhpcyBnZW5lcmFsIGxheW91dCwgd2UgY2FuIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgZnVuY3Rpb24gdGhhdCBleHRyYWN0cyB0aGlzIGluZm9ybWF0aW9uOg0KICANCmBgYHtyfQ0KZ2V0X3JlY2lwZSA8LSBmdW5jdGlvbih1cmwpew0KICAgIGggPC0gcmVhZF9odG1sKHVybCkNCiAgICByZWNpcGUgPC0gaCAlPiUgaHRtbF9ub2RlKCIuby1Bc3NldFRpdGxlX19hLUhlYWRsaW5lVGV4dCIpICU+JSBodG1sX3RleHQoKQ0KICAgIHByZXBfdGltZSA8LSBoICU+JSBodG1sX25vZGUoIi5vLVJlY2lwZUluZm9fX2EtRGVzY3JpcHRpb24tLVRvdGFsIikgJT4lIGh0bWxfdGV4dCgpDQogICAgaW5ncmVkaWVudHMgPC0gaCAlPiUgaHRtbF9ub2RlcygiLm8tSW5ncmVkaWVudHNfX2EtTGlzdEl0ZW1UZXh0IikgJT4lIGh0bWxfdGV4dCgpDQogICAgcmV0dXJuKGxpc3QocmVjaXBlID0gcmVjaXBlLCBwcmVwX3RpbWUgPSBwcmVwX3RpbWUsIGluZ3JlZGllbnRzID0gaW5ncmVkaWVudHMpKQ0KfQ0KDQpnZXRfcmVjaXBlKCJodHRwOi8vd3d3LmZvb2RuZXR3b3JrLmNvbS9yZWNpcGVzL2Zvb2QtbmV0d29yay1raXRjaGVuL3BhbmNha2VzLXJlY2lwZS0xOTEzODQ0IikNCmBgYA0KDQoNCkJlbG93IGlzIGFuIGV4YW1wbGUgZm9yIFNjcmFwaW5nIHJhaW4gZGF0YSBmcm9tIGEgd2Vic2l0ZToNCiAgDQpgYGB7cn0NCnllYXIgPC0gYygyMDEzOjIwMTcpDQpkYXRlIDwtIGMoMjAxMzAxOjIwMTMxMiwyMDE0MDE6MjAxNDEyLDIwMTUwMToyMDE1MTIsMjAxNjAxOjIwMTYxMiwyMDE3MDE6MjAxNzEyKQ0KdGQgPC0gZGF0YS5mcmFtZSgncmQnPU5BKQ0KdXJsIDwtIHBhc3RlMCgnaHR0cDovL2Utc2VydmljZS5jd2IuZ292LnR3L0hpc3RvcnlEYXRhUXVlcnkvWWVhckRhdGFDb250cm9sbGVyLmRvP2NvbW1hbmQ9dmlld01haW4mc3RhdGlvbj00NjY5MTAmc3RuYW1lPSUyNUU5JTI1OUUlMjU4RCUyNUU5JTI1ODMlMjVBOCZkYXRlcGlja2VyPScseWVhcikNCmZvciggaSBpbiAxOjUpew0KICByZCA8LSB1cmxbaV0gJT4lIA0KICAgIEdFVCgpICU+JSANCiAgICByZWFkX2h0bWwoKSAlPiUgDQogICAgaHRtbF9ub2RlcyguLGNzcz0ndGQ6bnRoLWNoaWxkKDIxKScpICU+JSANCiAgICBodG1sX3RleHQoKQ0KICByZCA8LSBkYXRhLmZyYW1lKCdyZCc9cmQpDQogIHRkIDwtIHJiaW5kKHRkLHJkKQ0KfQ0KdGQgPC0gdGRbLTEsXQ0KZDEgPC0gZGF0YS5mcmFtZSgnZGF0ZSc9ZGF0ZSkNCmQxIDwtIGNiaW5kKGQxLHRkKQ0KDQpkMSR0ZCA8LSBnc3ViKCJcVTAwQTAiLCAiIiwgZDEkdGQpDQpWaWV3KGQxKQ0KDQp3cml0ZS5jc3YoZDEsIGZpbGU9IkM6L1VzZXJzL3NoaWtpL0Rvd25sb2Fkcy8xLnhsc3giKQ0KYGBgDQoNCg0KPGJyPjxocj4NCg0KIyMjIDxhIGlkPSJuNCI+PC9hPuOAkFN0cmluZyBQcm9jZXNzaW5n44CRDQogIA0KYGBge3J9DQpzdHJfZGV0ZWN0KHRhYiRwb3B1bGF0aW9uLCAiLCIpICAgIyDlm57lgrNUUlVFL0ZBTFNFDQpzdHJfc3Vic2V0KHRhYiRwb3B1bGF0aW9uLCAiLCIpICAgIyDlm57lgrPlrZfkuLIgZXguIDEyMyw0NTYvMSwwMDANCnN0cl9leHRyYWN0KHRhYiRwb3B1bGF0aW9uLCAiLCIpICAjIOWbnuWCs+mAl+iZnyjmj5Dlj5YiIuijoemdoueahOadseilvykNCmBgYA0KDQo8YnI+PGJyPjxocj48YnI+PGJyPjxicj4NCiAgDQoNCjxzdHlsZT4NCnAsbGkgew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KdGl0bGV7DQogICAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmJvZHl7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpoMSxoMixoMyxoNCxoNXsNCiAgICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCjwvc3R5bGU+PGJyPjxicj48YnI+PGJyPjxicj4NCg0KPHN0eWxlPg0KLmNhcHRpb24gew0KICBjb2xvcjogIzc3NzsNCiAgbWFyZ2luLXRvcDogMTBweDsNCn0NCnAgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcHJlIHsNCiAgd29yZC1icmVhazogbm9ybWFsOw0KICB3b3JkLXdyYXA6IG5vcm1hbDsNCiAgbGluZS1oZWlnaHQ6IDE7DQp9DQpwcmUgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcCxsaSB7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQoucnsNCiAgbGluZS1oZWlnaHQ6IDEuMjsNCn0NCg0KdGl0bGV7DQogIGNvbG9yOiAjY2MwMDAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KYm9keXsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgxLGgyLGgzLGg0LGg1ew0KICBjb2xvcjogIzAwODgwMDsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgzew0KICBjb2xvcjogI2IzNmIwMDsNCiAgYmFja2dyb3VuZDogI2ZmZTBiMzsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoNXsNCiAgY29sb3I6ICMwMDYwMDA7DQogIGJhY2tncm91bmQ6ICNmZmZmZTA7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KZW17DQogIGNvbG9yOiAjMDAwMGMwOw0KICBiYWNrZ3JvdW5kOiAjZjBmMGYwOw0KICB9DQo8L3N0eWxlPg0KDQo=