Shortcuts Summary

R Markdown

Additional

General Introduction to R

R & RStudio

Apa itu R ?

Bahasa pemrograman sekaligus software yang memungkinkan pengolahan data secara statistik (statistical computing)But it’s more than that: supporting the whole data science flow + other purposes

source: https://www.storybench.org/getting-started-with-tidyverse-in-r

Why Learn R?

  • free & open source
  • cross platform → Windows, Linux, MacOS
  • multi purposes → not only for computational statistics.
  • outstanding visualization → ggplot & Shiny.
  • intuitive language.
  • vast community and package ecosystem
    • you can almost find what you need in The Comprehensive R Archive Network (CRAN)
  • used both in academic and industrial field.

R & RStudio: How Do They Relate?

  • R : Engine, RStudio : Dashboard
  • RStudio is essentially a user-friendly interface for R

Instalasi R & RStudio

RStudio cheatsheet collections: https://www.rstudio.com/resources/cheatsheets/

R Studio

  • IDE (Integrated Development Environment)
  • editor dengan fitur yang lengkap untuk mendukung pembangunan suatu script atau package R dengan lebih produktif.
  • 4 quadrants → can be arranged using menu ‘View’

Working Directory

  • check your current working directory
    • ketik getwd()
  • set your working directory
    • setwd(directory_path)
    • e.g: setwd(‘/run/media/DATA/myR/’)
  • Check your R version → R.version

Tambahan :

R Project An R project enables your work to be bundled in a portable, self-contained folder. Within the project, all the relevant scripts, data files, figures/outputs, and history are stored in sub-folders and importantly - the working directory is the project’s root folder.

Writing Your First R Code

Start Writing The Code + Help

# Print greeting
print("Welcome to R!")
[1] "Welcome to R!"
# Help
# ?thingstosearch

?print()
?print()
?cbind()
print("R is the coolest subject I've ever learned")
[1] "R is the coolest subject I've ever learned"
"R is the coolest subject I've ever learned"
[1] "R is the coolest subject I've ever learned"
# 'R is the coolest subject I've ever learned' #ERROR

R Packages

Collections of functions written by R’s talented community of developers.

Memperkaya fungsionalitas R dalam mengolah data.

contoh: tidyverse, ggplot, and RMySQL. → anything else??

  • INSTALL → get the packages available in your R environment

    • From https://cran.r-project.org .

      • install.packages(package_name)

      • or using GUI

    • From GitHub

      • install.packages(“devtools”)
      • devtools::install_github(“hadley/babynames”)
  • LOAD → get the package ready for our needs.

    • library(package_name)
    • or using GUI
  • See what packages we have in our R environment:

    • installed.packages()
    • library()
  • UNINSTALL PACKAGEremove.packages(package_name)

  • UNLOAD PACKAGEdetach(NAMA_PACKAGE, unload=true) or using GUI

try to install & load a package:

  1. readxl
  2. RMySQL
  3. mongolite
  4. jsonlite
  5. googlesheets4
  6. haven
  7. foreign

# Install Package

## Install package from CRAN
# install.packages('dplyr')

## Install package from github
# install.packages('devtools')
# devtools::install_github('hadley/babynames')

## List installed package
# installed.packages()

Operators

Arithmetic Operators

  • + : addition
  • - : substraction
  • * : multiply
  • / : division
  • ^ : power
  • %%: modulo

example

a <- 81
b <- 9
x1 <- a + b
x2 <- a - b
x3 <- b * b
x4 <- a / b
x5 <- b ^ 2

x <- 1:4
y <- 6:9
z <- x+y

Relational operators

  • == : equals to (note the double equals sign)
  • != : not equals to
  • > : greater than
  • >= : greater than or equal to
  • < : less than
  • <= : less than or equal to

example

a <- 81
b <- 9

a == b
[1] FALSE
a <= b 
[1] FALSE
a > b   
[1] TRUE
a - 72 == b
[1] TRUE
a - 72 > b
[1] FALSE
a - 72 >= b
[1] TRUE
x <- 1:4
x > 2
[1] FALSE FALSE  TRUE  TRUE

Logical operators

  • ! NOT
  • & AND
  • | OR

example

a <- 81
b <- 9

a != b
[1] TRUE
!(a == b)
[1] TRUE
(a - 72 == b) | (a - 72 > b)
[1] TRUE
(a - 72 == b) & (a - 72 > b)
[1] FALSE

Mathematical + String functions

Mathematical

  • cos() : cosine
  • exp() : exponential
  • log10() : logarithm (base 10)
  • log() : logarithm (base e)
  • sqrt() : square-root of 2
  • round() : round to decimal places
  • signif() : round to certain significant figures
  • floor() : round down
  • abs() : absolute value

String

  • paste()

example:

sqrt(16)
[1] 4
str1 = 'Hello'
str2 = 'World!'

# concatenate two strings using paste function
paste(str1,str2)
[1] "Hello World!"

Variables

  • nama variable bersifat case-sensitive
  • tidak boleh diawali dengan angka/simbol.
  • jika nama > 1 kata, sambung dengan -, _, atau .
  • Assignment atau pemberian nilai pada variable menggunakan operator <- atau =
  • yang bisa di-variablekan:
    • angka
    • text
    • object
    • formula, etc.
  • Tidak boleh menggunakan simbol seperti ^, !, $, @, +, -, /, %, or *:
## Assign variable
a <- 10
b <- 8

c <- a + b
d <- a ^ b
i <- sqrt(25)
j <- round(0.34567)
k <- factorial(5)

e <- 'Hello'
f <- 'World'
g <- paste(e, f)
g2 <- paste(e, f, sep=",")

Data Types

  • Tunggal (atomic)
    • character
    • numeric
    • categorical
    • logical (boolean)
    • Integer
    • date
    • Complex
    • Raw
  • Untuk melihat tipe data, gunakan fungsi class( ) atau typeof( ).
  • Untuk menguji tipe atomic di atas dari suatu data, maka gunakan
    • is.integer is.numeric is.character is.complex is.logical
  • Non Tunggal
    • 1D
      • Vector → semua element harus bertipe sama → v <- c(1, 2, 3, 4, 5)
      • List → element tidak harus bertipe sama.
        • l1 <- list(1, 2, “a”, “b”)
        • l2 <- list(1, “a”, 3, TRUE, c(5, 6, 7))
      • Factor → kategorikf <- factor(c("single", "married", "married", "single"));
    • 2D
      • table-like → Matrix, Dataframe, Tibble,

  • vector

    is what is called an array in all other programming languages except R — a collection of cells with a fixed size where all cells hold the same type (integers or characters or reals or whatever).

  • list

    can hold items of different types and the list size can be increased on the fly. List contents can be accessed either by index (like mylist[[1]]) or by name (like mylist$age).

  • matrix

    is a two-dimensional vector (fixed size, all cell types the same).

  • array

    is a vector with one or more dimensions. So, an array with one dimension is (almost) the same as a vector. An array with two dimensions is (almost) the same as a matrix. An array with three or more dimensions is an n-dimensional array.

  • data frame

    is called a table in most languages. Each column holds the same type, and the columns can have header names.

Example vector code:

v = c(1:3)  # a vector with [1.0 2.0 3.0]
cat(v, "\n\n")
1 2 3 
v = vector(mode="integer", 4)  # [0 0 0 0]
cat(v, "\n\n")
0 0 0 0 
v = c("a", "b", "x")
cat(v, "\n\n")
a b x 

Example list code:

ls = list("a", 2.2)
ls[3] = as.integer(3)
print(ls)
[[1]]
[1] "a"

[[2]]
[1] 2.2

[[3]]
[1] 3
cat(ls[[2]], "\n\n")
2.2 
ls = list(name="Smith", age=22)
cat(ls$name, ":", ls$age)
Smith : 22

Example matrix code:

m = matrix(0.0, nrow=2, ncol=3) # 2x3
print(m)
     [,1] [,2] [,3]
[1,]    0    0    0
[2,]    0    0    0

Example array code:

arr = array(0.0, 3)  # [0.0 0.0 0.0]
print(arr)
[1] 0 0 0
arr = array(0.0, c(2,3))  # 2x3 matrix
print(arr)
     [,1] [,2] [,3]
[1,]    0    0    0
[2,]    0    0    0
arr = array(0.0, c(2,5,4)) # 2x5x4 n-array
# print(arr)  # 40 values displayed

Example data frame code:

people = c("Alex", "Barb", "Carl") # col 1
ages = c(19, 29, 39)  # col 2
df = data.frame(people, ages)  # create
names(df) = c("NAME", "AGE")  # headers
print(df)

Vector

h <- 1:10
h_mult <- h*5

# get n-th element
h_mult[1]
[1] 5
# subset vector
h_mult[2:4]
[1] 10 15 20
# subsetting with subset()
subset(h_mult, h_mult > 20)
[1] 25 30 35 40 45 50
h <- c(1, 2, 3, 4, 5)
h2 <- c(1:5)

h_mult <- h*5

# get n-th element
h_mult[1]
[1] 5
# subset vector
h_mult[2:4]
[1] 10 15 20
# subsetting with subset()
subset(h_mult, h_mult > 20)
[1] 25
# menggunakan c( )
x <- c(1:10)
x <- c(1,2,3,4,5,6,7,8,9,10)

# menggunakan seq()
x <- seq(1,10,by=1)
# menggabungkan vector dengan rbind() dan cbind()
h <- c(1, 2, 3, 4, 5)
i <- c(2, 5)
rbind(h, i) # by row
Warning: number of columns of result is not a multiple of vector length (arg 2)
  [,1] [,2] [,3] [,4] [,5]
h    1    2    3    4    5
i    2    5    2    5    2
cbind(h, i) # by column
Warning: number of rows of result is not a multiple of vector length (arg 2)
     h i
[1,] 1 2
[2,] 2 5
[3,] 3 2
[4,] 4 5
[5,] 5 2
x <- c(1:5)
y <- c(6:10)
cbind(x, y)
     x  y
[1,] 1  6
[2,] 2  7
[3,] 3  8
[4,] 4  9
[5,] 5 10
rbind(x, y)
  [,1] [,2] [,3] [,4] [,5]
x    1    2    3    4    5
y    6    7    8    9   10

Mathematical Function

c <- c(1:10)
m <- mean(c)
m
[1] 5.5

List

  • Element di dalam-nya bisa berbeda tipe dan ukuran berbeda.

  • Element bisa tunggal, bisa juga berupa another list.

  • Dibuat dengan menggunakan fungsi list().

  • contoh:

```r
varlist <- list(nama="Pandora", jumlahbulan=4, bobotkelas=c(4,3,5,4))
varlist
```
```
$nama
[1] "Pandora"

$jumlahbulan
[1] 4

$bobotkelas
[1] 4 3 5 4
```
  • Akses isi list:

    • Menggunakan posisi indeks

      • kurung siku tunggal [ ] untuk mendapatkan list
      • kurung siku ganda [[ ]] untuk mendapatkan elemen dari list
    • Menggunakan operator $ berindex nama untuk mendapatkan element

  • unlist() → mengubah list menjadi vector

varlist <- list(nama="Pandora", jumlahbulan=4, bobotkelas=c(4,3,5,4))
varlist
$nama
[1] "Pandora"

$jumlahbulan
[1] 4

$bobotkelas
[1] 4 3 5 4
varlist[3]
$bobotkelas
[1] 4 3 5 4
varlist[[3]]
[1] 4 3 5 4
varlist$bobotkelas
[1] 4 3 5 4
# does not have to be the same type
list_a <- list("a", "b", "c")
list_a
[[1]]
[1] "a"

[[2]]
[1] "b"

[[3]]
[1] "c"
list_b <- list(1, 2, 3)
list_b
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 3
# ----------------------

list_c <- list(1, "a", TRUE)
list_c
[[1]]
[1] 1

[[2]]
[1] "a"

[[3]]
[1] TRUE
list_c[1]
[[1]]
[1] 1
list_c[[1]]
[1] 1
# ----------------------

list_d <- list(a = "x", b = "y", c = "z")
list_d
$a
[1] "x"

$b
[1] "y"

$c
[1] "z"
list_d["a"]
$a
[1] "x"
list_d[["a"]]
[1] "x"
list_d$a
[1] "x"

Factor

# categoric

# levelnya dari urutan alphabet
fact_a <- factor(c("zebra", "beruang", "macan"))
# levelnya ditentukan
fact_b <- factor(c("zebra", "beruang", "macan"), levels = c("macan", "beruang", "zebra"))

fact_a
[1] zebra   beruang macan  
Levels: beruang macan zebra
fact_b
[1] zebra   beruang macan  
Levels: macan beruang zebra
# levelnya urutan angka
fact_c <- factor(c(5, 10, 2))
fact_c
[1] 5  10 2 
Levels: 2 5 10
factor(c(50, 10, "macan"))
[1] 50    10    macan
Levels: 10 50 macan

Matrix

matrix_a = matrix(
  c(1, 2, 3, 4, 5, 6, 7, 8, 9),
  nrow = 3,  
  ncol = 3,
)
matrix_a
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

matrix_b = matrix(
  c(1, 2, 3, 4, 5, 6, 7, 8, 9),
  nrow = 3,  
  ncol = 3, 
  # By default matrices are in column-wise order
  byrow = T         
)
matrix_b
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

# Naming rows
rownames(matrix_a) = c("a", "b", "c")
# Naming columns
colnames(matrix_a) = c("d", "e", "f")

matrix_a
  d e f
a 1 4 7
b 2 5 8
c 3 6 9

# Naming rows
rownames(matrix_b) = c("a", "b", "c")
# Naming columns
colnames(matrix_b) = c("c", "d", "e")

matrix_b
  c d e
a 1 2 3
b 4 5 6
c 7 8 9

# Naming rows
rownames(matrix_b) = c("umur", "jumlah anak", "c")
# Naming columns
colnames(matrix_b) = c("bla", "bli", "blu")

matrix_b
            bla bli blu
umur          1   2   3
jumlah anak   4   5   6
c             7   8   9

Data Frame

Struktur dua dimensi seperti table dimana bisa terdiri dari beberapa kolom dan tiap kolom memiliki tipe data yang sama, dan total baris tiap kolom haruslah sama.

  • terdiri dari beberapa kolom

  • tipe data yang sama

  • total baris tiap kolom harus sama

  • Membuat data frame menggunakan fungsi data.frame

```r
data.transaksi <- data.frame( 
  kode_transaksi = c(1:5), 
  nama = c("Ari","Budi","Celine","Darmin","Erik") 
)

data.transaksi
```
<div data-pagedtable="false">
  <script data-pagedtable-source type="application/json">
{"columns":[{"label":["kode_transaksi"],"name":[1],"type":["int"],"align":["right"]},{"label":["nama"],"name":[2],"type":["chr"],"align":["left"]}],"data":[{"1":"1","2":"Ari"},{"1":"2","2":"Budi"},{"1":"3","2":"Celine"},{"1":"4","2":"Darmin"},{"1":"5","2":"Erik"}],"options":{"columns":{"min":{},"max":[10],"total":[2]},"rows":{"min":[10],"max":[10],"total":[5]},"pages":{}}}
  </script>
</div>
  • Akses isi data frame:

    • $ operator: Mengambil data pada kolom setelah operator $.
    • [m]: Mengambil kolom m.
    • [m, ]: Mengambil baris m, all columns.
    • [c(m,n),]: Mengambil baris m dan n, all columns.
    • [c(m:n),]: Mengambil baris m sampai n, all columns.
employee <- c('John Doe','Peter Gynn','Jolie Hope')
salary <- c(21000, 23400, 26800)
startdate <- as.Date(c('2010-11-1','2008-3-25','2007-3-14'))

employ.data <- data.frame(employee, salary, startdate)
employ.data

# View(employ.data) # must Capital
data(mtcars)
dim(mtcars)
mtcars[c(3:5), ]

Tibble

# tidyverse's dataframe
library("tidyverse")
── Attaching core tidyverse packages ───────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.0     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.1     ✔ tibble    3.2.0
✔ lubridate 1.9.2     ✔ tidyr     1.3.0
✔ purrr     1.0.1     ── Conflicts ─────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
employ.data <- as_tibble(employ.data)
employ.data

Check Data Types + Data Type Conversion

a <- 10
b <- c(1, 2, 3, 4, 5)

# data types
class(a)
[1] "numeric"
class(b)
[1] "numeric"
# structure
str(a)
 num 10
str(b)
 num [1:5] 1 2 3 4 5
# data types
x <- c(1, "a", 3, TRUE)
typeof(x)
[1] "character"
# check if data types -> is.*
is.numeric(10)
[1] TRUE
x <- c(1, 2, 3, 5)
class(x)
[1] "numeric"
typeof(x)
[1] "double"
# Data Type Conversion -> as.*
a = 100
str_a <- as.character(a)
num_a <- as.numeric(str_a)

as.numeric(c("-.1", " 2.7 ", "B"))
Warning: NAs introduced by coercion
[1] -0.1  2.7   NA

Acquiring & Inspecting Data in R

How to Acquire Data in R

  1. Internal
    • Embedded datasets in RStudio:

      • data() → from RStudio

      • data(package = .packages(all.available = TRUE)) → from packages installed in RStudio.

    • Create your own:

      • vector → c( )

        • tiga_angka <- c(1, 2, 3)

        • index start from 1 (not 0)

      • teks → diapit quote ” ”

        • teks <- "halo DQLab"
      • dataframe()

  2. External (import)

External Data

  • Jenis:
    • text-based
      • csv
      • other delimiter → read_delim()
    • excel → perlu install package readxl
    • json → perlu install package jsonlite
    • mysql → perlu install package RMySQL atau RMariaDB
    • mongoDB → perlu install package mongolite
    • googlesheets → perlu install package googlesheets4
    • SPSS, SAS, STATA → perlu install package haven atau foreign
    • etc.
  • Input:
    • local file → defined by local path
    • from www → defined by URL

Embedded datasets

# data()
# data(mtcars)
force(mtcars)

Data Frame

## Creating Data Frame
employee <- c('John Doe','Peter Gynn','Jolie Hope')
salary <- c(21000, 23400, 26800)
startdate <- as.Date(c('2010-11-1','2008-3-25','2007-3-14'))

employ.data <- data.frame(employee, salary, startdate)
str(employ.data)
'data.frame':   3 obs. of  3 variables:
 $ employee : chr  "John Doe" "Peter Gynn" "Jolie Hope"
 $ salary   : num  21000 23400 26800
 $ startdate: Date, format: "2010-11-01" "2008-03-25" "2007-03-14"

Import From CSV

library(readr)

# tidyverse -> read_csv
flavors_of_cacao <- read_csv('data/flavors_of_cacao.csv')
Rows: 1795 Columns: 9── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (6): Company 
(Maker-if known), Specific Bean Origin
or Bar Name, Cocoa
Percent, Company
Location, Be...
dbl (3): REF, Review
Date, Rating
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# base (bawaan R) -> read.csv
flavors_of_cacao <- read.csv('data/flavors_of_cacao.csv')

flavors_of_cacao

Import From Excel

library(readxl)
flavors_of_cacao <- read_xlsx('data/flavors_of_cacao.xlsx')
New names:
flavors_of_cacao

Import From JSON

library(jsonlite)
Warning: package ‘jsonlite’ was built under R version 4.2.3
Attaching package: ‘jsonlite’

The following object is masked from ‘package:purrr’:

    flatten
flavors_of_cacao <- fromJSON('data/flavors_of_cacao.json')
flavors_of_cacao

Import From Google Sheets

library(gsheet)
Warning: package ‘gsheet’ was built under R version 4.2.3
mtcars <- gsheet2tbl('docs.google.com/spreadsheets/d/1I9mJsS5QnXF2TNNntTy-HrcdHmIF9wJ8ONYvEJTXSNo')
mtcars

Import From Mongodb

# mongodb+srv://<username>:<password>@cluster0.uqpkp.mongodb.net/myFirstDatabase?retryWrites=true&w=majority

# library(mongolite)
# url <- "mongodb+srv://dqlab:wguifGPUKQZDYX6@cluster0.uqpkp.mongodb.net/dqlab?retryWrites=true&w=majority"
# flavors_of_cacao_4 <- mongo("flavors_of_cacao", url = url)$find()

Import From MySql/MariaDb

# library(RMariaDB)
# con <- dbConnect(
#   RMariaDB::MariaDB(), host = "0.0.0.0", port = 3307, username = "user", 
#   password = "password", dbname = "database")
# res <- dbSendQuery(con, "SELECT * FROM v2021_statement")
# mysql_data <- dbFetch(res)
# dbClearResult(res)
# library(RMySQL)
# con <- dbConnect(
#   RMySQL::MySQL(), host = "localhost", port = 3306, username = "root", 
#   password = "nurimammasri", dbname = "training")
# rs <- dbSendQuery(con, "SELECT * FROM ms_cabang")
# mysql_data <- dbFetch(rs)
# str(mysql_data)

Exercise

# 1. Read dataset from https://github.com/owid/covid-19-data/raw/master/public/data/vaccinations/vaccinations.csv into dataframe
vaccinations <- read_csv('https://github.com/owid/covid-19-data/raw/master/public/data/vaccinations/vaccinations.csv', show_col_types = FALSE)

# 2. read dataset from https://raw.githubusercontent.com/erikaris/dataset/main/indonesia_prov_iso.json
indonesia_prov_iso <- fromJSON('https://raw.githubusercontent.com/erikaris/dataset/main/indonesia_prov_iso.json')

# 3. read iris dataset from https://docs.google.com/spreadsheets/d/1fI9kwibZTpOGPGcTk30TJG740AMyB1KGRsRNrHFUPEw
iris <- gsheet2tbl('https://docs.google.com/spreadsheets/d/1fI9kwibZTpOGPGcTk30TJG740AMyB1KGRsRNrHFUPEw')

# 4. read mysql db from your previous sessions using RMariaDB. 
library(RMySQL)
Warning: package ‘RMySQL’ was built under R version 4.2.3Loading required package: DBI
con <- dbConnect(
  RMySQL::MySQL(), host = "localhost", port = 3306, username = "root", 
  password = "nurimammasri", dbname = "training")
rs <- dbSendQuery(con, "SELECT * FROM ms_cabang")
mysql_data <- dbFetch(rs)
str(mysql_data)
'data.frame':   500 obs. of  3 variables:
 $ kode_cabang: chr  "CABANG-001" "CABANG-002" "CABANG-003" "CABANG-004" ...
 $ nama_cabang: chr  "PHI Mini Market - Lhokseumawe 01" "PHI Mini Market - Bau-Bau 01" "PHI Mini Market - Bogor 01" "PHI Mini Market - Medan 01" ...
 $ kode_kota  : chr  "KOTA-003" "KOTA-083" "KOTA-039" "KOTA-007" ...
# con <- dbConnect(
#   RMariaDB::MariaDB(), host = "127.0.0.1", port = 3306, username = "root", dbname = "minimart")
# res <- dbSendQuery(con, "SELECT * FROM ms_people")
# mysql_data <- dbFetch(res)

Inspecting Your DataFrame

  • look the whole data: type the data name or View()

  • Look part of the data:

    • head() -> menampilkan 5 data pertama
    • tail() -> menampilkan 5 data terakhir
    • glimpse() -> menampilkan 5 data pertama
    • str() -> menampilkan struktur
  • Look at the dimension

    • dim()
    • nrow()
    • ncol()
    • length()
  • Look at the structure:

    • str()
    • class()
    • typeof()
    • names()
    • nchar()
  • Tambahan

    • table()
iris <- read.csv('data/iris_dataset.csv')
iris
# Look the whole data: type the data name or View()
# View(iris)
# . Look part of the data:
head(mtcars)
tail(mtcars)
# . Look at the structure:
library(dplyr)
glimpse(mtcars)
Rows: 32
Columns: 11
$ mpg  <dbl> 21.0, 21.0, 22.8, 21.4, 18.7, 18.1, 14.3, 24.4, 22.8, 19.2, 17.8, 16.4, 17.3, 15.2, 10.4, 10.4…
$ cyl  <dbl> 6, 6, 4, 6, 8, 6, 8, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 8, 8, 8, 8, 4, 4, 4, 8, 6, 8, 4
$ disp <dbl> 160.0, 160.0, 108.0, 258.0, 360.0, 225.0, 360.0, 146.7, 140.8, 167.6, 167.6, 275.8, 275.8, 275…
$ hp   <dbl> 110, 110, 93, 110, 175, 105, 245, 62, 95, 123, 123, 180, 180, 180, 205, 215, 230, 66, 52, 65, …
$ drat <dbl> 3.90, 3.90, 3.85, 3.08, 3.15, 2.76, 3.21, 3.69, 3.92, 3.92, 3.92, 3.07, 3.07, 3.07, 2.93, 3.00…
$ wt   <dbl> 2.620, 2.875, 2.320, 3.215, 3.440, 3.460, 3.570, 3.190, 3.150, 3.440, 3.440, 4.070, 3.730, 3.7…
$ qsec <dbl> 16.46, 17.02, 18.61, 19.44, 17.02, 20.22, 15.84, 20.00, 22.90, 18.30, 18.90, 17.40, 17.60, 18.…
$ vs   <dbl> 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1
$ am   <dbl> 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1
$ gear <dbl> 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 4
$ carb <dbl> 4, 4, 1, 1, 2, 1, 4, 2, 2, 4, 4, 3, 3, 3, 4, 4, 4, 1, 2, 1, 1, 2, 2, 4, 2, 1, 2, 2, 4, 6, 8, 2
str(mtcars)
spc_tbl_ [32 × 11] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ mpg : num [1:32] 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
 $ cyl : num [1:32] 6 6 4 6 8 6 8 4 4 6 ...
 $ disp: num [1:32] 160 160 108 258 360 ...
 $ hp  : num [1:32] 110 110 93 110 175 105 245 62 95 123 ...
 $ drat: num [1:32] 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
 $ wt  : num [1:32] 2.62 2.88 2.32 3.21 3.44 ...
 $ qsec: num [1:32] 16.5 17 18.6 19.4 17 ...
 $ vs  : num [1:32] 0 0 1 1 0 1 0 1 1 1 ...
 $ am  : num [1:32] 1 1 1 0 0 0 0 0 0 0 ...
 $ gear: num [1:32] 4 4 4 3 3 3 3 4 4 4 ...
 $ carb: num [1:32] 4 4 1 1 2 1 4 2 2 4 ...
 - attr(*, "spec")=
  .. cols(
  ..   mpg = col_double(),
  ..   cyl = col_double(),
  ..   disp = col_double(),
  ..   hp = col_double(),
  ..   drat = col_double(),
  ..   wt = col_double(),
  ..   qsec = col_double(),
  ..   vs = col_double(),
  ..   am = col_double(),
  ..   gear = col_double(),
  ..   carb = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 
# . Look at the dimension
dim(iris)
[1] 150   5
nrow(iris)
[1] 150
ncol(iris)
[1] 5
length(iris)
[1] 5
class(iris)
[1] "data.frame"
typeof(iris)
[1] "list"
names(iris)
[1] "sepal.length..cm." "sepal.width..cm."  "petal.length..cm." "petal.width..cm." 
[5] "target"           
employee <- c('John Doe','Peter Gynn','Jolie Hope')
salary <- c(21000, 23400, 26800)
startdate <- as.Date(c('2010-11-1','2008-3-25','2007-3-14'))

employ.data <- data.frame(employee, salary, startdate)
table(employ.data)
, , startdate = 2007-03-14

            salary
employee     21000 23400 26800
  John Doe       0     0     0
  Jolie Hope     0     0     1
  Peter Gynn     0     0     0

, , startdate = 2008-03-25

            salary
employee     21000 23400 26800
  John Doe       0     0     0
  Jolie Hope     0     0     0
  Peter Gynn     0     1     0

, , startdate = 2010-11-01

            salary
employee     21000 23400 26800
  John Doe       1     0     0
  Jolie Hope     0     0     0
  Peter Gynn     0     0     0
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IDcyDQotLS0NCg0KIyAqKlNob3J0Y3V0cyBTdW1tYXJ5KioNCg0KLSAgIENvbnRyb2wvQ3RybCArIDE6IFNvdXJjZSBlZGl0b3IgKHlvdXIgc2NyaXB0KQ0KDQotICAgQ29udHJvbC9DdHJsICsgMjogQ29uc29sZQ0KDQotICAgQ29udHJvbC9DdHJsICsgMzogSGVscA0KDQotICAgQ29udHJvbC9DdHJsICsgNDogSGlzdG9yeQ0KDQotICAgQ29udHJvbC9DdHJsICsgNTogRmlsZXMNCg0KLSAgIENvbnRyb2wvQ3RybCArIDY6IFBsb3RzDQoNCi0gICBDb250cm9sL0N0cmwgKyA3OiBQYWNrYWdlcw0KDQotICAgQ29udHJvbC9DdHJsICsgODogRW52aXJvbm1lbnQNCg0KLSAgIENvbnRyb2wvQ3RybCArIDk6IFZpZXdlcg0KDQotICAgVG9vbHMgXD4gS2V5Ym9hcmQgU2hvcnRjdXRzIEhlbHAgKEFsdCArIFNoaWZ0ICsgSykNCg0KLSAgIFw8LSAoQWx0ICsgLSApDQoNCi0gICAlXDwlIChDdHJsICsgU2hpZnQgKyBNKQ0KDQotICAgQ29tbWVudCBvciB1bmNvbW1lbnQgbGluZXMg4oaSIEN0cmwgKyBTaGlmdCArIEMNCg0KLSAgIE1vdmUgTGluZXMgVXAvRG93biDihpIgQWx0K1VwL0Rvd24NCg0KLSAgIERlbGV0ZSBMaW5lIOKGkiBDdHJsK0QNCg0KLSAgIFNlbGVjdCDihpIgU2hpZnQrW0Fycm93XQ0KDQotICAgU2VsZWN0IFdvcmQg4oaSIEN0cmwrU2hpZnQrTGVmdC9SaWdodA0KDQotICAgU2VsZWN0IHRvIExpbmUgU3RhcnQg4oaSIEFsdCtTaGlmdCtMZWZ0DQoNCi0gICBTZWxlY3QgdG8gTGluZSBFbmQg4oaSIEFsdCtTaGlmdCtSaWdodA0KDQotICAgUnVuIGN1cnJlbnQgbGluZSDihpIgQ3RybCArIEVudGVyDQoNCi0gICBSdW4gY3VycmVudCBsaW5lIChyZXRhaW4gY3Vyc29yIHBvc2l0aW9uKSDihpIgQWx0K0VudGVyDQoNCi0gICBSdW4gYWxsIGxpbmVzIG9mIGNvZGUg4oaSIEN0cmwgKyBBICsgRW50ZXINCg0KLSAgIENsZWFyIENvbnNvbGUg4oaSIEN0cmwrTA0KDQotICAgUmVzdGFydCB0aGUgY3VycmVudCBSIHNlc3Npb24g4oaSIEN0cmwgKyBTaGlmdCArIEYxMA0KDQotICAgU2VhcmNoIHRoZSBjb21tYW5kIGhpc3RvcnkgZnJvbSB0aGUgQ29uc29sZSDihpIgQ3RybCArIFt1cCBhcnJvd10pDQoNCi0gICBRdWlja2x5IEZpbmQgRmlsZXMgYW5kIEZ1bmN0aW9ucyDihpIgQ3RybCArIC4NCg0KLSAgIE9wZW4gRmlsZXMg4oaSIEN0cmwrTw0KDQotICAgTmV3IEZpbGUgUiBTY3JpcHQg4oaSIEN0cmwrU2hpZnQrTg0KDQoqKlIgTWFya2Rvd24qKg0KDQotICAgSW5zZXJ0IFIgY2h1bmsg4oaSIEN0cmwrQWx0K0kNCg0KKipBZGRpdGlvbmFsKioNCg0KLSAgIEZpbmQgYW5kIEFkZCBOZXh0IOKGkiBDdHJsICsgRA0KDQojICoqR2VuZXJhbCBJbnRyb2R1Y3Rpb24gdG8gUioqDQoNCiMjICoqUiAmIFJTdHVkaW8qKg0KDQojIyMgKipBcGEgaXR1IFIgPyoqDQoNCioqQmFoYXNhIHBlbXJvZ3JhbWFuKiogc2VrYWxpZ3VzICoqc29mdHdhcmUqKiB5YW5nIG1lbXVuZ2tpbmthbg0KcGVuZ29sYWhhbiBkYXRhIHNlY2FyYSBzdGF0aXN0aWsgKiooc3RhdGlzdGljYWwgY29tcHV0aW5nKSoqIOKGkiAqKkJ1dA0KaXQncyBtb3JlIHRoYW4gdGhhdDogc3VwcG9ydGluZyB0aGUgd2hvbGUgZGF0YSBzY2llbmNlIGZsb3cgKyBvdGhlcg0KcHVycG9zZXMqKg0KDQohW10oYXNzZXRzL3RpZHl2ZXJzZS03Njh4MzEwLnBuZykNCg0Kc291cmNlOiA8aHR0cHM6Ly93d3cuc3RvcnliZW5jaC5vcmcvZ2V0dGluZy1zdGFydGVkLXdpdGgtdGlkeXZlcnNlLWluLXI+DQoNCiMjIyAqKldoeSBMZWFybiBSPyoqDQoNCi0gICBmcmVlICYgb3BlbiBzb3VyY2UNCi0gICBjcm9zcyBwbGF0Zm9ybSDihpIgKldpbmRvd3MsIExpbnV4LCBNYWNPUyoNCi0gICBtdWx0aSBwdXJwb3NlcyDihpIgKm5vdCBvbmx5IGZvciBjb21wdXRhdGlvbmFsIHN0YXRpc3RpY3MuKg0KLSAgIG91dHN0YW5kaW5nIHZpc3VhbGl6YXRpb24g4oaSICpnZ3Bsb3QgJiBTaGlueS4qDQotICAgaW50dWl0aXZlIGxhbmd1YWdlLg0KLSAgIHZhc3QgY29tbXVuaXR5IGFuZCBwYWNrYWdlIGVjb3N5c3RlbQ0KICAgIC0gICB5b3UgY2FuIGFsbW9zdCBmaW5kIHdoYXQgeW91IG5lZWQgaW4gVGhlIENvbXByZWhlbnNpdmUgUiBBcmNoaXZlDQogICAgICAgIE5ldHdvcmsgKENSQU4pDQotICAgdXNlZCBib3RoIGluIGFjYWRlbWljIGFuZCBpbmR1c3RyaWFsIGZpZWxkLg0KDQojIyMgKipSICYgUlN0dWRpbzogSG93IERvIFRoZXkgUmVsYXRlPyoqDQoNCi0gICAqKlIgOioqIEVuZ2luZSwgKipSU3R1ZGlvIDoqKiBEYXNoYm9hcmQNCi0gICAqKlJTdHVkaW8qKiBpcyBlc3NlbnRpYWxseSBhIHVzZXItZnJpZW5kbHkgaW50ZXJmYWNlIGZvciAqKlIqKg0KDQojIyMgKipJbnN0YWxhc2kgUiAmIFJTdHVkaW8qKg0KDQotICAgSW5zdGFsbCBSDQogICAgLSAgIGRvd25sb2FkIFIgZnJvbSA8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvPg0KICAgIC0gICBpbnN0YWxsDQotICAgSW5zdGFsbCBSU3R1ZGlvOg0KICAgIC0gICBkb3dubG9hZCBSU3R1ZGlvIERlc2t0b3AgZnJvbQ0KICAgICAgICA8aHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby8+DQogICAgLSAgIEluc3RhbGwNCi0gICBGb3Igbm9uIFdpbmRvd3MgT1MgKExpbnV4IG9yIE1hY09TKSwgaW5zdGFsbCBSICYgUlN0dWRpbyB1c2luZw0KICAgIHBhY2thZ2UgbWFuYWdlciBvciB0ZXJtaW5hbC4NCg0KKipSU3R1ZGlvIGNoZWF0c2hlZXQgY29sbGVjdGlvbnMqKjoNCjxodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvPg0KDQojIyMgKipSIFN0dWRpbyoqDQoNCi0gICBJREUgKEludGVncmF0ZWQgRGV2ZWxvcG1lbnQgRW52aXJvbm1lbnQpDQotICAgZWRpdG9yIGRlbmdhbiBmaXR1ciB5YW5nIGxlbmdrYXAgdW50dWsgbWVuZHVrdW5nIHBlbWJhbmd1bmFuIHN1YXR1DQogICAgc2NyaXB0IGF0YXUgcGFja2FnZSBSIGRlbmdhbiBsZWJpaCBwcm9kdWt0aWYuDQotICAgNCBxdWFkcmFudHMg4oaSIGNhbiBiZSBhcnJhbmdlZCB1c2luZyBtZW51ICdWaWV3Jw0KDQohW10oYXNzZXRzL1JTdHVkaW8ucG5nKXt3aWR0aD0iNTI3In0NCg0KKipXb3JraW5nIERpcmVjdG9yeSoqDQoNCi0gICBjaGVjayB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkNCiAgICAtICAga2V0aWsgYGdldHdkKClgDQotICAgc2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkNCiAgICAtICAgYHNldHdkKGRpcmVjdG9yeV9wYXRoKWANCiAgICAtICAgZS5nOiBgc2V0d2Qo4oCYL3J1bi9tZWRpYS9EQVRBL215Ui/igJkpYA0KLSAgIENoZWNrIHlvdXIgUiB2ZXJzaW9uIOKGkiBgUi52ZXJzaW9uYA0KDQohW10oYXNzZXRzL3NldHdkLnBuZyl7d2lkdGg9IjU1OCJ9DQoNClRhbWJhaGFuIDoNCg0KKipSIFByb2plY3QqKiBBbiBSIHByb2plY3QgKiplbmFibGVzIHlvdXIgd29yayB0byBiZSBidW5kbGVkIGluIGENCnBvcnRhYmxlLCBzZWxmLWNvbnRhaW5lZCBmb2xkZXIqKi4gV2l0aGluIHRoZSBwcm9qZWN0LCBhbGwgdGhlIHJlbGV2YW50DQpzY3JpcHRzLCBkYXRhIGZpbGVzLCBmaWd1cmVzL291dHB1dHMsIGFuZCBoaXN0b3J5IGFyZSBzdG9yZWQgaW4NCnN1Yi1mb2xkZXJzIGFuZCBpbXBvcnRhbnRseSAtIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBpcyB0aGUgcHJvamVjdCdzDQpyb290IGZvbGRlci4NCg0KIyAqKldyaXRpbmcgWW91ciBGaXJzdCBSIENvZGUqKg0KDQoqKlN0YXJ0IFdyaXRpbmcgVGhlIENvZGUgKyBIZWxwKioNCg0KLSAgIENyZWF0ZSBhIG5ldyBSIGZpbGU6IEZpbGUgXD4gTmV3IEZpbGUgXD4gUiBTY3JpcHQNCi0gICBMZXQncyBzdGFydCB3aXRoIHBsYXlpbmcgd2l0aCB2YXJpb3VzIFIgb3BlcmF0b3JzDQoNCmBgYHtyfQ0KIyBQcmludCBncmVldGluZw0KcHJpbnQoIldlbGNvbWUgdG8gUiEiKQ0KDQojIEhlbHANCiMgP3RoaW5nc3Rvc2VhcmNoDQoNCj9wcmludCgpDQo/cHJpbnQoKQ0KP2NiaW5kKCkNCmBgYA0KDQpgYGB7cn0NCnByaW50KCJSIGlzIHRoZSBjb29sZXN0IHN1YmplY3QgSSd2ZSBldmVyIGxlYXJuZWQiKQ0KIlIgaXMgdGhlIGNvb2xlc3Qgc3ViamVjdCBJJ3ZlIGV2ZXIgbGVhcm5lZCINCiMgJ1IgaXMgdGhlIGNvb2xlc3Qgc3ViamVjdCBJJ3ZlIGV2ZXIgbGVhcm5lZCcgI0VSUk9SDQpgYGANCg0KIyMgKipSIFBhY2thZ2VzKioNCg0KQ29sbGVjdGlvbnMgb2YgZnVuY3Rpb25zIHdyaXR0ZW4gYnkgUidzIHRhbGVudGVkIGNvbW11bml0eSBvZg0KZGV2ZWxvcGVycy4NCg0KTWVtcGVya2F5YSBmdW5nc2lvbmFsaXRhcyBSIGRhbGFtIG1lbmdvbGFoIGRhdGEuDQoNCmNvbnRvaDogKip0aWR5dmVyc2UsIGdncGxvdCwgYW5kIFJNeVNRTCoqLiDihpIgYW55dGhpbmcgZWxzZT8/DQoNCi0gICAqKklOU1RBTEwqKiDihpIgZ2V0IHRoZSBwYWNrYWdlcyBhdmFpbGFibGUgaW4geW91ciBSIGVudmlyb25tZW50DQoNCiAgICAtICAgRnJvbSA8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmc+IC4NCg0KICAgICAgICAtICAgKippbnN0YWxsLnBhY2thZ2VzKHBhY2thZ2VfbmFtZSkqKg0KDQogICAgICAgIC0gICBvciB1c2luZyBHVUkNCg0KICAgIC0gICBGcm9tIEdpdEh1Yg0KDQogICAgICAgIC0gICAqKmluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikqKg0KICAgICAgICAtICAgKipkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImhhZGxleS9iYWJ5bmFtZXMiKSoqDQoNCi0gICAqKkxPQUQqKiDihpIgZ2V0IHRoZSBwYWNrYWdlIHJlYWR5IGZvciBvdXIgbmVlZHMuDQoNCiAgICAtICAgKipsaWJyYXJ5KHBhY2thZ2VfbmFtZSkqKg0KICAgIC0gICBvciB1c2luZyBHVUkNCg0KLSAgIFNlZSB3aGF0IHBhY2thZ2VzIHdlIGhhdmUgaW4gb3VyIFIgZW52aXJvbm1lbnQ6DQoNCiAgICAtICAgKippbnN0YWxsZWQucGFja2FnZXMoKSoqDQogICAgLSAgICoqbGlicmFyeSgpKioNCg0KLSAgICoqVU5JTlNUQUxMIFBBQ0tBR0UqKiDihpIgKipyZW1vdmUucGFja2FnZXMocGFja2FnZV9uYW1lKSoqDQoNCi0gICAqKlVOTE9BRCBQQUNLQUdFKiog4oaSICoqZGV0YWNoKE5BTUFfUEFDS0FHRSwgdW5sb2FkPXRydWUpKiogb3IgdXNpbmcNCiAgICBHVUkNCg0KKip0cnkgdG8gaW5zdGFsbCAmIGxvYWQgYSBwYWNrYWdlOioqDQoNCjEuICByZWFkeGwNCjIuICBSTXlTUUwNCjMuICBtb25nb2xpdGUNCjQuICBqc29ubGl0ZQ0KNS4gIGdvb2dsZXNoZWV0czQNCjYuICBoYXZlbg0KNy4gIGZvcmVpZ24NCg0KIVtdKGFzc2V0cy9pbXBvcnRhbnQtcGNrZy5wbmcpe3dpZHRoPSIzNTYifQ0KDQpgYGB7cn0NCiMgSW5zdGFsbCBQYWNrYWdlDQoNCiMjIEluc3RhbGwgcGFja2FnZSBmcm9tIENSQU4NCiMgaW5zdGFsbC5wYWNrYWdlcygnZHBseXInKQ0KDQojIyBJbnN0YWxsIHBhY2thZ2UgZnJvbSBnaXRodWINCiMgaW5zdGFsbC5wYWNrYWdlcygnZGV2dG9vbHMnKQ0KIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ2hhZGxleS9iYWJ5bmFtZXMnKQ0KDQojIyBMaXN0IGluc3RhbGxlZCBwYWNrYWdlDQojIGluc3RhbGxlZC5wYWNrYWdlcygpDQpgYGANCg0KIyMgKipPcGVyYXRvcnMqKg0KDQojIyMgKipBcml0aG1ldGljIE9wZXJhdG9ycyoqDQoNCi0gICBcKyA6IGFkZGl0aW9uDQotICAgXC0gOiBzdWJzdHJhY3Rpb24NCi0gICBcKiA6IG11bHRpcGx5DQotICAgLyA6IGRpdmlzaW9uDQotICAgXF4gOiBwb3dlcg0KLSAgICUlOiBtb2R1bG8NCg0KZXhhbXBsZQ0KDQpgYGB7cn0NCmEgPC0gODENCmIgPC0gOQ0KeDEgPC0gYSArIGINCngyIDwtIGEgLSBiDQp4MyA8LSBiICogYg0KeDQgPC0gYSAvIGINCng1IDwtIGIgXiAyDQoNCnggPC0gMTo0DQp5IDwtIDY6OQ0KeiA8LSB4K3kNCmBgYA0KDQojIyMgKipSZWxhdGlvbmFsIG9wZXJhdG9ycyoqDQoNCi0gICA9PSA6IGVxdWFscyB0byAobm90ZSB0aGUgZG91YmxlIGVxdWFscyBzaWduKQ0KLSAgICE9IDogbm90IGVxdWFscyB0bw0KLSAgIFw+IDogZ3JlYXRlciB0aGFuDQotICAgXD49IDogZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvDQotICAgXDwgOiBsZXNzIHRoYW4NCi0gICBcPD0gOiBsZXNzIHRoYW4gb3IgZXF1YWwgdG8NCg0KZXhhbXBsZQ0KDQpgYGB7cn0NCmEgPC0gODENCmIgPC0gOQ0KDQphID09IGINCmEgPD0gYiANCmEgPiBiICAgDQoNCmEgLSA3MiA9PSBiDQphIC0gNzIgPiBiDQphIC0gNzIgPj0gYg0KYGBgDQoNCmBgYHtyfQ0KeCA8LSAxOjQNCnggPiAyDQpgYGANCg0KIyMjICoqTG9naWNhbCBvcGVyYXRvcnMqKg0KDQotICAgISAqKk5PVCoqDQotICAgJiAqKkFORCoqDQotICAgXHwgKipPUioqDQoNCmV4YW1wbGUNCg0KYGBge3J9DQphIDwtIDgxDQpiIDwtIDkNCg0KYSAhPSBiDQohKGEgPT0gYikNCihhIC0gNzIgPT0gYikgfCAoYSAtIDcyID4gYikNCihhIC0gNzIgPT0gYikgJiAoYSAtIDcyID4gYikNCmBgYA0KDQojIyMgKipNYXRoZW1hdGljYWwgKyBTdHJpbmcgZnVuY3Rpb25zKioNCg0KKipNYXRoZW1hdGljYWwqKg0KDQotICAgKipjb3MoKSoqIDogY29zaW5lDQotICAgKipleHAoKSoqIDogZXhwb25lbnRpYWwNCi0gICAqKmxvZzEwKCkqKiA6IGxvZ2FyaXRobSAoYmFzZSAxMCkNCi0gICAqKmxvZygpKiogOiBsb2dhcml0aG0gKGJhc2UgZSkNCi0gICAqKnNxcnQoKSoqIDogc3F1YXJlLXJvb3Qgb2YgMg0KLSAgICoqcm91bmQoKSoqIDogcm91bmQgdG8gZGVjaW1hbCBwbGFjZXMNCi0gICAqKnNpZ25pZigpKiogOiByb3VuZCB0byBjZXJ0YWluIHNpZ25pZmljYW50IGZpZ3VyZXMNCi0gICAqKmZsb29yKCkqKiA6IHJvdW5kIGRvd24NCi0gICAqKmFicygpKiogOiBhYnNvbHV0ZSB2YWx1ZQ0KDQoqKlN0cmluZyoqDQoNCi0gICAqKnBhc3RlKCkqKg0KDQoqKmV4YW1wbGU6KioNCg0KYGBge3J9DQpzcXJ0KDE2KQ0KYGBgDQoNCmBgYHtyfQ0Kc3RyMSA9ICdIZWxsbycNCnN0cjIgPSAnV29ybGQhJw0KDQojIGNvbmNhdGVuYXRlIHR3byBzdHJpbmdzIHVzaW5nIHBhc3RlIGZ1bmN0aW9uDQpwYXN0ZShzdHIxLHN0cjIpDQpgYGANCg0KIyMgKipWYXJpYWJsZXMqKg0KDQotICAgbmFtYSB2YXJpYWJsZSBiZXJzaWZhdCAqKmNhc2Utc2Vuc2l0aXZlKioNCi0gICAqKnRpZGFrIGJvbGVoIGRpYXdhbGkqKiBkZW5nYW4gKiphbmdrYS9zaW1ib2wqKi4NCi0gICBqaWthIG5hbWEgKipcPiAxIGthdGEqKiwgc2FtYnVuZyBkZW5nYW4gKiotLCBcXywgYXRhdSAuKioNCi0gICAqKkFzc2lnbm1lbnQgYXRhdSBwZW1iZXJpYW4gbmlsYWkqKiBwYWRhIHZhcmlhYmxlIG1lbmdndW5ha2FuDQogICAgKipvcGVyYXRvciBcPC0gYXRhdSA9KioNCi0gICB5YW5nIGJpc2EgZGktdmFyaWFibGVrYW46DQogICAgLSAgIGFuZ2thDQogICAgLSAgIHRleHQNCiAgICAtICAgb2JqZWN0DQogICAgLSAgIGZvcm11bGEsIGV0Yy4NCi0gICAqKlRpZGFrIGJvbGVoIG1lbmdndW5ha2FuIHNpbWJvbCoqIHNlcGVydGkgKipcXiwgISwgXCQsIFxALCArLCAtLCAvLA0KICAgICUsIG9yIFwqOioqDQoNCmBgYHtyfQ0KIyMgQXNzaWduIHZhcmlhYmxlDQphIDwtIDEwDQpiIDwtIDgNCg0KYyA8LSBhICsgYg0KZCA8LSBhIF4gYg0KaSA8LSBzcXJ0KDI1KQ0KaiA8LSByb3VuZCgwLjM0NTY3KQ0KayA8LSBmYWN0b3JpYWwoNSkNCg0KZSA8LSAnSGVsbG8nDQpmIDwtICdXb3JsZCcNCmcgPC0gcGFzdGUoZSwgZikNCmcyIDwtIHBhc3RlKGUsIGYsIHNlcD0iLCIpDQpgYGANCg0KIyMgKipEYXRhIFR5cGVzKioNCg0KLSAgICoqVHVuZ2dhbCAoYXRvbWljKSoqDQogICAgLSAgIGNoYXJhY3Rlcg0KICAgIC0gICBudW1lcmljDQogICAgLSAgIGNhdGVnb3JpY2FsDQogICAgLSAgIGxvZ2ljYWwgKGJvb2xlYW4pDQogICAgLSAgIEludGVnZXINCiAgICAtICAgZGF0ZQ0KICAgIC0gICBDb21wbGV4DQogICAgLSAgIFJhdw0KLSAgICoqVW50dWsgbWVsaWhhdCB0aXBlIGRhdGEqKiwgZ3VuYWthbiBmdW5nc2kgKipjbGFzcyggKSoqIGF0YXUNCiAgICAqKnR5cGVvZiggKS4qKg0KLSAgIFVudHVrIG1lbmd1amkgdGlwZSBhdG9taWMgZGkgYXRhcyBkYXJpIHN1YXR1IGRhdGEsIG1ha2EgZ3VuYWthbg0KICAgIC0gICAqKmlzLmludGVnZXIgaXMubnVtZXJpYyBpcy5jaGFyYWN0ZXIgaXMuY29tcGxleCBpcy5sb2dpY2FsKioNCi0gICAqKk5vbiBUdW5nZ2FsKioNCiAgICAtICAgKioxRCoqDQogICAgICAgIC0gICAqKlZlY3RvcioqIOKGkiBzZW11YSBlbGVtZW50IGhhcnVzIGJlcnRpcGUgc2FtYSDihpINCiAgICAgICAgICAgIGB2IDwtIGMoMSwgMiwgMywgNCwgNSlgDQogICAgICAgIC0gICAqKkxpc3QqKiDihpIgZWxlbWVudCB0aWRhayBoYXJ1cyBiZXJ0aXBlIHNhbWEuDQogICAgICAgICAgICAtICAgYGwxIDwtIGxpc3QoMSwgMiwg4oCcYeKAnSwg4oCcYuKAnSlgDQogICAgICAgICAgICAtICAgYGwyIDwtIGxpc3QoMSwg4oCcYeKAnSwgMywgVFJVRSwgYyg1LCA2LCA3KSlgDQogICAgICAgIC0gICAqKkZhY3RvciDihpIga2F0ZWdvcmlrKiog4oaSDQogICAgICAgICAgICBgZiA8LSBmYWN0b3IoYygic2luZ2xlIiwgIm1hcnJpZWQiLCAibWFycmllZCIsICJzaW5nbGUiKSk7YA0KICAgIC0gICAqKjJEKioNCiAgICAgICAgLSAgIHRhYmxlLWxpa2Ug4oaSICoqTWF0cml4LCBEYXRhZnJhbWUsIFRpYmJsZSwqKg0KDQohW10oYXNzZXRzL3NldC1kYXRhLnBuZyl7d2lkdGg9IjQ4NiJ9DQoNCi0gICAqKnZlY3RvcioqDQoNCiAgICBpcyB3aGF0IGlzIGNhbGxlZCBhbiBhcnJheSBpbiBhbGwgb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIGV4Y2VwdA0KICAgIFIgLS0tIGEgY29sbGVjdGlvbiBvZiBjZWxscyB3aXRoIGEgKipmaXhlZCBzaXplIHdoZXJlIGFsbCBjZWxscyBob2xkDQogICAgdGhlIHNhbWUgdHlwZSoqIChpbnRlZ2VycyBvciBjaGFyYWN0ZXJzIG9yIHJlYWxzIG9yIHdoYXRldmVyKS4NCg0KLSAgICoqbGlzdCoqDQoNCiAgICBjYW4gaG9sZCBpdGVtcyBvZiAqKmRpZmZlcmVudCB0eXBlcyBhbmQgdGhlIGxpc3Qgc2l6ZSBjYW4gYmUNCiAgICBpbmNyZWFzZWQgb24gdGhlIGZseSoqLiBMaXN0IGNvbnRlbnRzIGNhbiBiZSBhY2Nlc3NlZCBlaXRoZXIgYnkNCiAgICBpbmRleCAobGlrZSBteWxpc3RbWzFdXSkgb3IgYnkgbmFtZSAobGlrZSBteWxpc3RcJGFnZSkuDQoNCi0gICAqKm1hdHJpeCoqDQoNCiAgICBpcyBhICoqdHdvLWRpbWVuc2lvbmFsIHZlY3RvciAoZml4ZWQgc2l6ZSwgYWxsIGNlbGwgdHlwZXMgdGhlDQogICAgc2FtZSkuKioNCg0KLSAgICoqYXJyYXkqKg0KDQogICAgaXMgYSB2ZWN0b3Igd2l0aCBvbmUgb3IgbW9yZSBkaW1lbnNpb25zLiBTbywgYW4gYXJyYXkgd2l0aCBvbmUNCiAgICBkaW1lbnNpb24gaXMgKGFsbW9zdCkgdGhlIHNhbWUgYXMgYSB2ZWN0b3IuIEFuIGFycmF5IHdpdGggdHdvDQogICAgZGltZW5zaW9ucyBpcyAoYWxtb3N0KSB0aGUgc2FtZSBhcyBhIG1hdHJpeC4gQW4gYXJyYXkgd2l0aCB0aHJlZSBvcg0KICAgIG1vcmUgZGltZW5zaW9ucyBpcyBhbiBuLWRpbWVuc2lvbmFsIGFycmF5Lg0KDQotICAgKipkYXRhIGZyYW1lKioNCg0KICAgIGlzIGNhbGxlZCBhIHRhYmxlIGluIG1vc3QgbGFuZ3VhZ2VzLiBFYWNoIGNvbHVtbiBob2xkcyB0aGUgKipzYW1lDQogICAgdHlwZSoqLCBhbmQgdGhlIGNvbHVtbnMgKipjYW4gaGF2ZSBoZWFkZXIgbmFtZXMqKi4NCg0KKipFeGFtcGxlIHZlY3RvciBjb2RlOioqDQoNCmBgYHtyfQ0KdiA9IGMoMTozKSAgIyBhIHZlY3RvciB3aXRoIFsxLjAgMi4wIDMuMF0NCmNhdCh2LCAiXG5cbiIpDQoNCnYgPSB2ZWN0b3IobW9kZT0iaW50ZWdlciIsIDQpICAjIFswIDAgMCAwXQ0KY2F0KHYsICJcblxuIikNCg0KdiA9IGMoImEiLCAiYiIsICJ4IikNCmNhdCh2LCAiXG5cbiIpDQpgYGANCg0KRXhhbXBsZSBsaXN0IGNvZGU6DQoNCmBgYHtyfQ0KbHMgPSBsaXN0KCJhIiwgMi4yKQ0KbHNbM10gPSBhcy5pbnRlZ2VyKDMpDQpwcmludChscykNCg0KY2F0KGxzW1syXV0sICJcblxuIikNCg0KbHMgPSBsaXN0KG5hbWU9IlNtaXRoIiwgYWdlPTIyKQ0KY2F0KGxzJG5hbWUsICI6IiwgbHMkYWdlKQ0KYGBgDQoNCkV4YW1wbGUgbWF0cml4IGNvZGU6DQoNCmBgYHtyfQ0KbSA9IG1hdHJpeCgwLjAsIG5yb3c9MiwgbmNvbD0zKSAjIDJ4Mw0KcHJpbnQobSkNCmBgYA0KDQpFeGFtcGxlIGFycmF5IGNvZGU6DQoNCmBgYHtyfQ0KYXJyID0gYXJyYXkoMC4wLCAzKSAgIyBbMC4wIDAuMCAwLjBdDQpwcmludChhcnIpDQoNCmFyciA9IGFycmF5KDAuMCwgYygyLDMpKSAgIyAyeDMgbWF0cml4DQpwcmludChhcnIpDQoNCmFyciA9IGFycmF5KDAuMCwgYygyLDUsNCkpICMgMng1eDQgbi1hcnJheQ0KIyBwcmludChhcnIpICAjIDQwIHZhbHVlcyBkaXNwbGF5ZWQNCmBgYA0KDQpFeGFtcGxlIGRhdGEgZnJhbWUgY29kZToNCg0KYGBge3J9DQpwZW9wbGUgPSBjKCJBbGV4IiwgIkJhcmIiLCAiQ2FybCIpICMgY29sIDENCmFnZXMgPSBjKDE5LCAyOSwgMzkpICAjIGNvbCAyDQpkZiA9IGRhdGEuZnJhbWUocGVvcGxlLCBhZ2VzKSAgIyBjcmVhdGUNCm5hbWVzKGRmKSA9IGMoIk5BTUUiLCAiQUdFIikgICMgaGVhZGVycw0KcHJpbnQoZGYpDQpgYGANCg0KIyMgKipWZWN0b3IqKg0KDQpgYGB7cn0NCmggPC0gMToxMA0KaF9tdWx0IDwtIGgqNQ0KDQojIGdldCBuLXRoIGVsZW1lbnQNCmhfbXVsdFsxXQ0KDQojIHN1YnNldCB2ZWN0b3INCmhfbXVsdFsyOjRdDQoNCiMgc3Vic2V0dGluZyB3aXRoIHN1YnNldCgpDQpzdWJzZXQoaF9tdWx0LCBoX211bHQgPiAyMCkNCmBgYA0KDQpgYGB7cn0NCmggPC0gYygxLCAyLCAzLCA0LCA1KQ0KaDIgPC0gYygxOjUpDQoNCmhfbXVsdCA8LSBoKjUNCg0KIyBnZXQgbi10aCBlbGVtZW50DQpoX211bHRbMV0NCg0KIyBzdWJzZXQgdmVjdG9yDQpoX211bHRbMjo0XQ0KDQojIHN1YnNldHRpbmcgd2l0aCBzdWJzZXQoKQ0Kc3Vic2V0KGhfbXVsdCwgaF9tdWx0ID4gMjApDQpgYGANCg0KYGBge3J9DQojIG1lbmdndW5ha2FuIGMoICkNCnggPC0gYygxOjEwKQ0KeCA8LSBjKDEsMiwzLDQsNSw2LDcsOCw5LDEwKQ0KDQojIG1lbmdndW5ha2FuIHNlcSgpDQp4IDwtIHNlcSgxLDEwLGJ5PTEpDQpgYGANCg0KYGBge3J9DQojIG1lbmdnYWJ1bmdrYW4gdmVjdG9yIGRlbmdhbiByYmluZCgpIGRhbiBjYmluZCgpDQpoIDwtIGMoMSwgMiwgMywgNCwgNSkNCmkgPC0gYygyLCA1KQ0KcmJpbmQoaCwgaSkgIyBieSByb3cNCmNiaW5kKGgsIGkpICMgYnkgY29sdW1uDQpgYGANCg0KYGBge3J9DQp4IDwtIGMoMTo1KQ0KeSA8LSBjKDY6MTApDQpjYmluZCh4LCB5KQ0KcmJpbmQoeCwgeSkNCmBgYA0KDQoqKk1hdGhlbWF0aWNhbCBGdW5jdGlvbioqDQoNCmBgYHtyfQ0KYyA8LSBjKDE6MTApDQptIDwtIG1lYW4oYykNCm0NCmBgYA0KDQojIyAqKkxpc3QqKg0KDQotICAgRWxlbWVudCBkaSBkYWxhbS1ueWEgKipiaXNhIGJlcmJlZGEgdGlwZSoqIGRhbiAqKnVrdXJhbiBiZXJiZWRhLioqDQoNCi0gICBFbGVtZW50IGJpc2EgKip0dW5nZ2FsKiosIGJpc2EganVnYSBiZXJ1cGEgKiphbm90aGVyIGxpc3QuKioNCg0KLSAgIERpYnVhdCBkZW5nYW4gbWVuZ2d1bmFrYW4gZnVuZ3NpICoqbGlzdCgpLioqDQoNCi0gICBjb250b2g6DQoNCiAgICBgYGB7cn0NCiAgICB2YXJsaXN0IDwtIGxpc3QobmFtYT0iUGFuZG9yYSIsIGp1bWxhaGJ1bGFuPTQsIGJvYm90a2VsYXM9Yyg0LDMsNSw0KSkNCiAgICB2YXJsaXN0DQogICAgYGBgDQoNCi0gICBBa3NlcyBpc2kgbGlzdDoNCg0KICAgIC0gICBNZW5nZ3VuYWthbiBwb3Npc2kgaW5kZWtzDQoNCiAgICAgICAgLSAgICoqa3VydW5nIHNpa3UgdHVuZ2dhbCBbIF0qKiB1bnR1ayBtZW5kYXBhdGthbiAqKmxpc3QqKg0KICAgICAgICAtICAgKiprdXJ1bmcgc2lrdSBnYW5kYSBbWyBdXSoqIHVudHVrIG1lbmRhcGF0a2FuICoqZWxlbWVuIGRhcmkNCiAgICAgICAgICAgIGxpc3QqKg0KDQogICAgLSAgIE1lbmdndW5ha2FuICoqb3BlcmF0b3IgXCQgYmVyaW5kZXggbmFtYSoqIHVudHVrIG1lbmRhcGF0a2FuDQogICAgICAgICoqZWxlbWVudCoqDQoNCi0gICAqKnVubGlzdCgpIOKGkiBtZW5ndWJhaCBsaXN0IG1lbmphZGkgdmVjdG9yKioNCg0KYGBge3J9DQp2YXJsaXN0IDwtIGxpc3QobmFtYT0iUGFuZG9yYSIsIGp1bWxhaGJ1bGFuPTQsIGJvYm90a2VsYXM9Yyg0LDMsNSw0KSkNCnZhcmxpc3QNCg0KdmFybGlzdFszXQ0KdmFybGlzdFtbM11dDQp2YXJsaXN0JGJvYm90a2VsYXMNCmBgYA0KDQpgYGB7cn0NCiMgZG9lcyBub3QgaGF2ZSB0byBiZSB0aGUgc2FtZSB0eXBlDQpsaXN0X2EgPC0gbGlzdCgiYSIsICJiIiwgImMiKQ0KbGlzdF9hDQoNCmxpc3RfYiA8LSBsaXN0KDEsIDIsIDMpDQpsaXN0X2INCg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmxpc3RfYyA8LSBsaXN0KDEsICJhIiwgVFJVRSkNCmxpc3RfYw0KDQpsaXN0X2NbMV0NCmxpc3RfY1tbMV1dDQoNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpsaXN0X2QgPC0gbGlzdChhID0gIngiLCBiID0gInkiLCBjID0gInoiKQ0KbGlzdF9kDQoNCmxpc3RfZFsiYSJdDQpsaXN0X2RbWyJhIl1dDQoNCmxpc3RfZCRhDQpgYGANCg0KIyMgKipGYWN0b3IqKg0KDQpgYGB7cn0NCiMgY2F0ZWdvcmljDQoNCiMgbGV2ZWxueWEgZGFyaSB1cnV0YW4gYWxwaGFiZXQNCmZhY3RfYSA8LSBmYWN0b3IoYygiemVicmEiLCAiYmVydWFuZyIsICJtYWNhbiIpKQ0KIyBsZXZlbG55YSBkaXRlbnR1a2FuDQpmYWN0X2IgPC0gZmFjdG9yKGMoInplYnJhIiwgImJlcnVhbmciLCAibWFjYW4iKSwgbGV2ZWxzID0gYygibWFjYW4iLCAiYmVydWFuZyIsICJ6ZWJyYSIpKQ0KDQpmYWN0X2ENCmZhY3RfYg0KDQojIGxldmVsbnlhIHVydXRhbiBhbmdrYQ0KZmFjdF9jIDwtIGZhY3RvcihjKDUsIDEwLCAyKSkNCmZhY3RfYw0KDQpmYWN0b3IoYyg1MCwgMTAsICJtYWNhbiIpKQ0KYGBgDQoNCiMjIE1hdHJpeA0KDQpgYGB7cn0NCm1hdHJpeF9hID0gbWF0cml4KA0KICBjKDEsIDIsIDMsIDQsIDUsIDYsIDcsIDgsIDkpLA0KICBucm93ID0gMywgIA0KICBuY29sID0gMywNCikNCm1hdHJpeF9hDQoNCmBgYA0KDQpgYGB7cn0NCg0KbWF0cml4X2IgPSBtYXRyaXgoDQogIGMoMSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSksDQogIG5yb3cgPSAzLCAgDQogIG5jb2wgPSAzLCANCiAgIyBCeSBkZWZhdWx0IG1hdHJpY2VzIGFyZSBpbiBjb2x1bW4td2lzZSBvcmRlcg0KICBieXJvdyA9IFQgICAgICAgICANCikNCm1hdHJpeF9iDQpgYGANCg0KYGBge3J9DQoNCiMgTmFtaW5nIHJvd3MNCnJvd25hbWVzKG1hdHJpeF9hKSA9IGMoImEiLCAiYiIsICJjIikNCiMgTmFtaW5nIGNvbHVtbnMNCmNvbG5hbWVzKG1hdHJpeF9hKSA9IGMoImQiLCAiZSIsICJmIikNCg0KbWF0cml4X2ENCmBgYA0KDQpgYGB7cn0NCg0KIyBOYW1pbmcgcm93cw0Kcm93bmFtZXMobWF0cml4X2IpID0gYygiYSIsICJiIiwgImMiKQ0KIyBOYW1pbmcgY29sdW1ucw0KY29sbmFtZXMobWF0cml4X2IpID0gYygiYyIsICJkIiwgImUiKQ0KDQptYXRyaXhfYg0KYGBgDQoNCmBgYHtyfQ0KDQojIE5hbWluZyByb3dzDQpyb3duYW1lcyhtYXRyaXhfYikgPSBjKCJ1bXVyIiwgImp1bWxhaCBhbmFrIiwgImMiKQ0KIyBOYW1pbmcgY29sdW1ucw0KY29sbmFtZXMobWF0cml4X2IpID0gYygiYmxhIiwgImJsaSIsICJibHUiKQ0KDQptYXRyaXhfYg0KYGBgDQoNCiMjICoqRGF0YSBGcmFtZSoqDQoNClN0cnVrdHVyIGR1YSBkaW1lbnNpIHNlcGVydGkgdGFibGUgZGltYW5hIGJpc2EgdGVyZGlyaSBkYXJpICoqYmViZXJhcGENCmtvbG9tKiogZGFuIHRpYXAga29sb20gbWVtaWxpa2kgKip0aXBlIGRhdGEgeWFuZyBzYW1hKiosIGRhbiAqKnRvdGFsDQpiYXJpcyB0aWFwIGtvbG9tIGhhcnVzbGFoIHNhbWEqKi4NCg0KLSAgICoqdGVyZGlyaSBkYXJpIGJlYmVyYXBhIGtvbG9tKioNCg0KLSAgICoqdGlwZSBkYXRhIHlhbmcgc2FtYSoqDQoNCi0gICAqKnRvdGFsIGJhcmlzIHRpYXAga29sb20gaGFydXMgc2FtYSoqDQoNCi0gICBNZW1idWF0IGRhdGEgZnJhbWUgbWVuZ2d1bmFrYW4gKipmdW5nc2kgZGF0YS5mcmFtZSoqDQoNCiAgICBgYGB7cn0NCiAgICBkYXRhLnRyYW5zYWtzaSA8LSBkYXRhLmZyYW1lKCANCiAgICAgIGtvZGVfdHJhbnNha3NpID0gYygxOjUpLCANCiAgICAgIG5hbWEgPSBjKCJBcmkiLCJCdWRpIiwiQ2VsaW5lIiwiRGFybWluIiwiRXJpayIpIA0KICAgICkNCg0KICAgIGRhdGEudHJhbnNha3NpDQogICAgYGBgDQoNCi0gICBBa3NlcyBpc2kgZGF0YSBmcmFtZToNCg0KICAgIC0gICAqKlwkIG9wZXJhdG9yOioqIE1lbmdhbWJpbCBkYXRhIHBhZGEga29sb20gc2V0ZWxhaCBvcGVyYXRvciBcJC4NCiAgICAtICAgKipbbV06KiogTWVuZ2FtYmlsIGtvbG9tIG0uDQogICAgLSAgICoqW20sIF06KiogTWVuZ2FtYmlsIGJhcmlzIG0sIGFsbCBjb2x1bW5zLg0KICAgIC0gICAqKltjKG0sbiksXToqKiBNZW5nYW1iaWwgYmFyaXMgbSBkYW4gbiwgYWxsIGNvbHVtbnMuDQogICAgLSAgICoqW2MobTpuKSxdOioqIE1lbmdhbWJpbCBiYXJpcyBtIHNhbXBhaSBuLCBhbGwgY29sdW1ucy4NCg0KYGBge3J9DQplbXBsb3llZSA8LSBjKCdKb2huIERvZScsJ1BldGVyIEd5bm4nLCdKb2xpZSBIb3BlJykNCnNhbGFyeSA8LSBjKDIxMDAwLCAyMzQwMCwgMjY4MDApDQpzdGFydGRhdGUgPC0gYXMuRGF0ZShjKCcyMDEwLTExLTEnLCcyMDA4LTMtMjUnLCcyMDA3LTMtMTQnKSkNCg0KZW1wbG95LmRhdGEgPC0gZGF0YS5mcmFtZShlbXBsb3llZSwgc2FsYXJ5LCBzdGFydGRhdGUpDQplbXBsb3kuZGF0YQ0KDQojIFZpZXcoZW1wbG95LmRhdGEpICMgbXVzdCBDYXBpdGFsDQpgYGANCg0KYGBge3J9DQpkYXRhKG10Y2FycykNCmRpbShtdGNhcnMpDQptdGNhcnNbYygzOjUpLCBdDQpgYGANCg0KIyMgKipUaWJibGUqKg0KDQpgYGB7cn0NCiMgdGlkeXZlcnNlJ3MgZGF0YWZyYW1lDQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQ0KZW1wbG95LmRhdGEgPC0gYXNfdGliYmxlKGVtcGxveS5kYXRhKQ0KZW1wbG95LmRhdGENCmBgYA0KDQojIyAqKkNoZWNrIERhdGEgVHlwZXMgKyBEYXRhIFR5cGUgQ29udmVyc2lvbioqDQoNCmBgYHtyfQ0KYSA8LSAxMA0KYiA8LSBjKDEsIDIsIDMsIDQsIDUpDQoNCiMgZGF0YSB0eXBlcw0KY2xhc3MoYSkNCmNsYXNzKGIpDQoNCiMgc3RydWN0dXJlDQpzdHIoYSkNCnN0cihiKQ0KDQojIGRhdGEgdHlwZXMNCnggPC0gYygxLCAiYSIsIDMsIFRSVUUpDQp0eXBlb2YoeCkNCg0KIyBjaGVjayBpZiBkYXRhIHR5cGVzIC0+IGlzLioNCmlzLm51bWVyaWMoMTApDQpgYGANCg0KYGBge3J9DQp4IDwtIGMoMSwgMiwgMywgNSkNCmNsYXNzKHgpDQp0eXBlb2YoeCkNCmBgYA0KDQpgYGB7cn0NCiMgRGF0YSBUeXBlIENvbnZlcnNpb24gLT4gYXMuKg0KYSA9IDEwMA0Kc3RyX2EgPC0gYXMuY2hhcmFjdGVyKGEpDQpudW1fYSA8LSBhcy5udW1lcmljKHN0cl9hKQ0KDQphcy5udW1lcmljKGMoIi0uMSIsICIgMi43ICIsICJCIikpDQoNCg0KYGBgDQoNCiMgKipBY3F1aXJpbmcgJiBJbnNwZWN0aW5nIERhdGEgaW4gUioqDQoNCiMjICoqSG93IHRvIEFjcXVpcmUgRGF0YSBpbiBSKioNCg0KMS4gICoqSW50ZXJuYWwqKg0KICAgIC0gICBFbWJlZGRlZCBkYXRhc2V0cyBpbiBSU3R1ZGlvOg0KDQogICAgICAgIC0gICAqKmRhdGEoKSoqIOKGkiBmcm9tIFJTdHVkaW8NCg0KICAgICAgICAtICAgKipkYXRhKHBhY2thZ2UgPSAucGFja2FnZXMoYWxsLmF2YWlsYWJsZSA9IFRSVUUpKSoqIOKGkiBmcm9tDQogICAgICAgICAgICBwYWNrYWdlcyBpbnN0YWxsZWQgaW4gUlN0dWRpby4NCg0KICAgIC0gICBDcmVhdGUgeW91ciBvd246DQoNCiAgICAgICAgLSAgICoqdmVjdG9yIOKGkiBjKCApKioNCg0KICAgICAgICAgICAgLSAgIGB0aWdhX2FuZ2thIDwtIGMoMSwgMiwgMylgDQoNCiAgICAgICAgICAgIC0gICBpbmRleCBzdGFydCBmcm9tIDEgKG5vdCAwKQ0KDQogICAgICAgIC0gICAqKnRla3MqKiDihpIgZGlhcGl0IHF1b3RlICIgIg0KDQogICAgICAgICAgICAtICAgYHRla3MgPC0gImhhbG8gRFFMYWIiYA0KDQogICAgICAgIC0gICAqKmRhdGFmcmFtZSgpKioNCjIuICAqKkV4dGVybmFsIChpbXBvcnQpKioNCg0KIyMgKipFeHRlcm5hbCBEYXRhKioNCg0KLSAgICoqSmVuaXM6KioNCiAgICAtICAgKip0ZXh0LWJhc2VkKioNCiAgICAgICAgLSAgICoqY3N2KioNCiAgICAgICAgLSAgIG90aGVyIGRlbGltaXRlciDihpIgKipgcmVhZF9kZWxpbSgpYCoqDQogICAgLSAgICoqZXhjZWwqKiDihpIgcGVybHUgaW5zdGFsbCBwYWNrYWdlICoqYHJlYWR4bGAqKg0KICAgIC0gICAqKmpzb24qKiDihpIgcGVybHUgaW5zdGFsbCBwYWNrYWdlICoqYGpzb25saXRlYCoqDQogICAgLSAgICoqbXlzcWwqKiDihpIgcGVybHUgaW5zdGFsbCBwYWNrYWdlICoqYFJNeVNRTGAqKiBhdGF1DQogICAgICAgICoqYFJNYXJpYURCYCoqDQogICAgLSAgICoqbW9uZ29EQioqIOKGkiBwZXJsdSBpbnN0YWxsIHBhY2thZ2UgKipgbW9uZ29saXRlYCoqDQogICAgLSAgICoqZ29vZ2xlc2hlZXRzKiog4oaSIHBlcmx1IGluc3RhbGwgcGFja2FnZSAqKmBnb29nbGVzaGVldHM0YCoqDQogICAgLSAgICoqU1BTUywgU0FTLCBTVEFUQSoqIOKGkiBwZXJsdSBpbnN0YWxsIHBhY2thZ2UgKipgaGF2ZW5gKiogYXRhdQ0KICAgICAgICAqKmBmb3JlaWduYCoqDQogICAgLSAgIGV0Yy4NCi0gICAqKklucHV0OioqDQogICAgLSAgICoqbG9jYWwgZmlsZSoqIOKGkiBkZWZpbmVkIGJ5IGxvY2FsIHBhdGgNCiAgICAtICAgKipmcm9tIHd3dyoqIOKGkiBkZWZpbmVkIGJ5IFVSTA0KDQoqKkVtYmVkZGVkIGRhdGFzZXRzKioNCg0KYGBge3J9DQojIGRhdGEoKQ0KIyBkYXRhKG10Y2FycykNCmZvcmNlKG10Y2FycykNCmBgYA0KDQoqKkRhdGEgRnJhbWUqKg0KDQpgYGB7cn0NCiMjIENyZWF0aW5nIERhdGEgRnJhbWUNCmVtcGxveWVlIDwtIGMoJ0pvaG4gRG9lJywnUGV0ZXIgR3lubicsJ0pvbGllIEhvcGUnKQ0Kc2FsYXJ5IDwtIGMoMjEwMDAsIDIzNDAwLCAyNjgwMCkNCnN0YXJ0ZGF0ZSA8LSBhcy5EYXRlKGMoJzIwMTAtMTEtMScsJzIwMDgtMy0yNScsJzIwMDctMy0xNCcpKQ0KDQplbXBsb3kuZGF0YSA8LSBkYXRhLmZyYW1lKGVtcGxveWVlLCBzYWxhcnksIHN0YXJ0ZGF0ZSkNCnN0cihlbXBsb3kuZGF0YSkNCmBgYA0KDQoqKkltcG9ydCBGcm9tIENTVioqDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCg0KIyB0aWR5dmVyc2UgLT4gcmVhZF9jc3YNCmZsYXZvcnNfb2ZfY2FjYW8gPC0gcmVhZF9jc3YoJ2RhdGEvZmxhdm9yc19vZl9jYWNhby5jc3YnKQ0KDQojIGJhc2UgKGJhd2FhbiBSKSAtPiByZWFkLmNzdg0KZmxhdm9yc19vZl9jYWNhbyA8LSByZWFkLmNzdignZGF0YS9mbGF2b3JzX29mX2NhY2FvLmNzdicpDQoNCmZsYXZvcnNfb2ZfY2FjYW8NCmBgYA0KDQoqKkltcG9ydCBGcm9tIEV4Y2VsKioNCg0KYGBge3J9DQpsaWJyYXJ5KHJlYWR4bCkNCmZsYXZvcnNfb2ZfY2FjYW8gPC0gcmVhZF94bHN4KCdkYXRhL2ZsYXZvcnNfb2ZfY2FjYW8ueGxzeCcpDQpmbGF2b3JzX29mX2NhY2FvDQpgYGANCg0KKipJbXBvcnQgRnJvbSBKU09OKioNCg0KYGBge3J9DQpsaWJyYXJ5KGpzb25saXRlKQ0KZmxhdm9yc19vZl9jYWNhbyA8LSBmcm9tSlNPTignZGF0YS9mbGF2b3JzX29mX2NhY2FvLmpzb24nKQ0KZmxhdm9yc19vZl9jYWNhbw0KYGBgDQoNCioqSW1wb3J0IEZyb20gR29vZ2xlIFNoZWV0cyoqDQoNCmBgYHtyfQ0KbGlicmFyeShnc2hlZXQpDQptdGNhcnMgPC0gZ3NoZWV0MnRibCgnZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFJOW1Kc1M1UW5YRjJUTk5udFR5LUhyY2RIbUlGOXdKOE9OWXZFSlRYU05vJykNCm10Y2Fycw0KYGBgDQoNCioqSW1wb3J0IEZyb20gTW9uZ29kYioqDQoNCmBgYHtyfQ0KIyBtb25nb2RiK3NydjovLzx1c2VybmFtZT46PHBhc3N3b3JkPkBjbHVzdGVyMC51cXBrcC5tb25nb2RiLm5ldC9teUZpcnN0RGF0YWJhc2U/cmV0cnlXcml0ZXM9dHJ1ZSZ3PW1ham9yaXR5DQoNCiMgbGlicmFyeShtb25nb2xpdGUpDQojIHVybCA8LSAibW9uZ29kYitzcnY6Ly9kcWxhYjp3Z3VpZkdQVUtRWkRZWDZAY2x1c3RlcjAudXFwa3AubW9uZ29kYi5uZXQvZHFsYWI/cmV0cnlXcml0ZXM9dHJ1ZSZ3PW1ham9yaXR5Ig0KIyBmbGF2b3JzX29mX2NhY2FvXzQgPC0gbW9uZ28oImZsYXZvcnNfb2ZfY2FjYW8iLCB1cmwgPSB1cmwpJGZpbmQoKQ0KYGBgDQoNCioqSW1wb3J0IEZyb20gTXlTcWwvTWFyaWFEYioqDQoNCmBgYHtyfQ0KIyBsaWJyYXJ5KFJNYXJpYURCKQ0KIyBjb24gPC0gZGJDb25uZWN0KA0KIyAgIFJNYXJpYURCOjpNYXJpYURCKCksIGhvc3QgPSAiMC4wLjAuMCIsIHBvcnQgPSAzMzA3LCB1c2VybmFtZSA9ICJ1c2VyIiwgDQojICAgcGFzc3dvcmQgPSAicGFzc3dvcmQiLCBkYm5hbWUgPSAiZGF0YWJhc2UiKQ0KIyByZXMgPC0gZGJTZW5kUXVlcnkoY29uLCAiU0VMRUNUICogRlJPTSB2MjAyMV9zdGF0ZW1lbnQiKQ0KIyBteXNxbF9kYXRhIDwtIGRiRmV0Y2gocmVzKQ0KIyBkYkNsZWFyUmVzdWx0KHJlcykNCmBgYA0KDQpgYGB7cn0NCiMgbGlicmFyeShSTXlTUUwpDQojIGNvbiA8LSBkYkNvbm5lY3QoDQojICAgUk15U1FMOjpNeVNRTCgpLCBob3N0ID0gImxvY2FsaG9zdCIsIHBvcnQgPSAzMzA2LCB1c2VybmFtZSA9ICJyb290IiwgDQojICAgcGFzc3dvcmQgPSAibnVyaW1hbW1hc3JpIiwgZGJuYW1lID0gInRyYWluaW5nIikNCiMgcnMgPC0gZGJTZW5kUXVlcnkoY29uLCAiU0VMRUNUICogRlJPTSBtc19jYWJhbmciKQ0KIyBteXNxbF9kYXRhIDwtIGRiRmV0Y2gocnMpDQojIHN0cihteXNxbF9kYXRhKQ0KYGBgDQoNCioqRXhlcmNpc2UqKg0KDQpgYGB7cn0NCiMgMS4gUmVhZCBkYXRhc2V0IGZyb20gaHR0cHM6Ly9naXRodWIuY29tL293aWQvY292aWQtMTktZGF0YS9yYXcvbWFzdGVyL3B1YmxpYy9kYXRhL3ZhY2NpbmF0aW9ucy92YWNjaW5hdGlvbnMuY3N2IGludG8gZGF0YWZyYW1lDQp2YWNjaW5hdGlvbnMgPC0gcmVhZF9jc3YoJ2h0dHBzOi8vZ2l0aHViLmNvbS9vd2lkL2NvdmlkLTE5LWRhdGEvcmF3L21hc3Rlci9wdWJsaWMvZGF0YS92YWNjaW5hdGlvbnMvdmFjY2luYXRpb25zLmNzdicsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpDQoNCiMgMi4gcmVhZCBkYXRhc2V0IGZyb20gaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2VyaWthcmlzL2RhdGFzZXQvbWFpbi9pbmRvbmVzaWFfcHJvdl9pc28uanNvbg0KaW5kb25lc2lhX3Byb3ZfaXNvIDwtIGZyb21KU09OKCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZXJpa2FyaXMvZGF0YXNldC9tYWluL2luZG9uZXNpYV9wcm92X2lzby5qc29uJykNCg0KIyAzLiByZWFkIGlyaXMgZGF0YXNldCBmcm9tIGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFmSTlrd2liWlRwT0dQR2NUazMwVEpHNzQwQU15QjFLR1JzUk5ySEZVUEV3DQppcmlzIDwtIGdzaGVldDJ0YmwoJ2h0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFmSTlrd2liWlRwT0dQR2NUazMwVEpHNzQwQU15QjFLR1JzUk5ySEZVUEV3JykNCg0KIyA0LiByZWFkIG15c3FsIGRiIGZyb20geW91ciBwcmV2aW91cyBzZXNzaW9ucyB1c2luZyBSTWFyaWFEQi4gDQpsaWJyYXJ5KFJNeVNRTCkNCmNvbiA8LSBkYkNvbm5lY3QoDQogIFJNeVNRTDo6TXlTUUwoKSwgaG9zdCA9ICJsb2NhbGhvc3QiLCBwb3J0ID0gMzMwNiwgdXNlcm5hbWUgPSAicm9vdCIsIA0KICBwYXNzd29yZCA9ICJudXJpbWFtbWFzcmkiLCBkYm5hbWUgPSAidHJhaW5pbmciKQ0KcnMgPC0gZGJTZW5kUXVlcnkoY29uLCAiU0VMRUNUICogRlJPTSBtc19jYWJhbmciKQ0KbXlzcWxfZGF0YSA8LSBkYkZldGNoKHJzKQ0Kc3RyKG15c3FsX2RhdGEpDQoNCiMgY29uIDwtIGRiQ29ubmVjdCgNCiMgICBSTWFyaWFEQjo6TWFyaWFEQigpLCBob3N0ID0gIjEyNy4wLjAuMSIsIHBvcnQgPSAzMzA2LCB1c2VybmFtZSA9ICJyb290IiwgZGJuYW1lID0gIm1pbmltYXJ0IikNCiMgcmVzIDwtIGRiU2VuZFF1ZXJ5KGNvbiwgIlNFTEVDVCAqIEZST00gbXNfcGVvcGxlIikNCiMgbXlzcWxfZGF0YSA8LSBkYkZldGNoKHJlcykNCmBgYA0KDQojIyAqKkluc3BlY3RpbmcgWW91ciBEYXRhRnJhbWUqKg0KDQotICAgbG9vayB0aGUgd2hvbGUgZGF0YTogdHlwZSB0aGUgZGF0YSBuYW1lIG9yIFZpZXcoKQ0KDQotICAgTG9vayBwYXJ0IG9mIHRoZSBkYXRhOg0KDQogICAgLSAgIGhlYWQoKSAtXD4gbWVuYW1waWxrYW4gNSBkYXRhIHBlcnRhbWENCiAgICAtICAgdGFpbCgpIC1cPiBtZW5hbXBpbGthbiA1IGRhdGEgdGVyYWtoaXINCiAgICAtICAgZ2xpbXBzZSgpIC1cPiBtZW5hbXBpbGthbiA1IGRhdGEgcGVydGFtYQ0KICAgIC0gICBzdHIoKSAtXD4gbWVuYW1waWxrYW4gc3RydWt0dXINCg0KLSAgIExvb2sgYXQgdGhlIGRpbWVuc2lvbg0KDQogICAgLSAgIGRpbSgpDQogICAgLSAgIG5yb3coKQ0KICAgIC0gICBuY29sKCkNCiAgICAtICAgbGVuZ3RoKCkNCg0KLSAgIExvb2sgYXQgdGhlIHN0cnVjdHVyZToNCg0KICAgIC0gICBzdHIoKQ0KICAgIC0gICBjbGFzcygpDQogICAgLSAgIHR5cGVvZigpDQogICAgLSAgIG5hbWVzKCkNCiAgICAtICAgbmNoYXIoKQ0KDQotICAgVGFtYmFoYW4NCg0KICAgIC0gICB0YWJsZSgpDQoNCmBgYHtyfQ0KaXJpcyA8LSByZWFkLmNzdignZGF0YS9pcmlzX2RhdGFzZXQuY3N2JykNCmlyaXMNCmBgYA0KDQpgYGB7cn0NCiMgTG9vayB0aGUgd2hvbGUgZGF0YTogdHlwZSB0aGUgZGF0YSBuYW1lIG9yIFZpZXcoKQ0KIyBWaWV3KGlyaXMpDQpgYGANCg0KYGBge3J9DQojIC4gTG9vayBwYXJ0IG9mIHRoZSBkYXRhOg0KaGVhZChtdGNhcnMpDQp0YWlsKG10Y2FycykNCmBgYA0KDQpgYGB7cn0NCiMgLiBMb29rIGF0IHRoZSBzdHJ1Y3R1cmU6DQpsaWJyYXJ5KGRwbHlyKQ0KZ2xpbXBzZShtdGNhcnMpDQoNCnN0cihtdGNhcnMpDQpgYGANCg0KYGBge3J9DQojIC4gTG9vayBhdCB0aGUgZGltZW5zaW9uDQpkaW0oaXJpcykNCm5yb3coaXJpcykNCm5jb2woaXJpcykNCmxlbmd0aChpcmlzKQ0KYGBgDQoNCmBgYHtyfQ0KY2xhc3MoaXJpcykNCnR5cGVvZihpcmlzKQ0KbmFtZXMoaXJpcykNCmBgYA0KDQpgYGB7cn0NCmVtcGxveWVlIDwtIGMoJ0pvaG4gRG9lJywnUGV0ZXIgR3lubicsJ0pvbGllIEhvcGUnKQ0Kc2FsYXJ5IDwtIGMoMjEwMDAsIDIzNDAwLCAyNjgwMCkNCnN0YXJ0ZGF0ZSA8LSBhcy5EYXRlKGMoJzIwMTAtMTEtMScsJzIwMDgtMy0yNScsJzIwMDctMy0xNCcpKQ0KDQplbXBsb3kuZGF0YSA8LSBkYXRhLmZyYW1lKGVtcGxveWVlLCBzYWxhcnksIHN0YXJ0ZGF0ZSkNCnRhYmxlKGVtcGxveS5kYXRhKQ0KYGBgDQo=