How to use this document. Make notes on your own!
Comments start with # (R ignores them). Actually run the
code along with me. Each section mirrors the live lab; code chunks show
the command and its output together.
Today’s plan
- Getting started with RStudio
- Installing & loading packages
- Working directory setup
- Basic R commands
- Working with data frames
- Histograms and boxplots
- Visualization with
ggplot2
Getting Started with
RStudio
RStudio is organized into four panes: Source |
Console | Environment | Files/Plots/Packages/Help.
Tip. Customize RStudio under Tools → Global
Options → Appearance / Pane Layout.
Running code. Click the Run button,
or use the keyboard shortcuts:
- Mac:
cmd + return
- Windows:
ctrl + enter
- If your cursor is anywhere on a single line, RStudio executes just
that line.
- If you highlight (select) multiple lines, RStudio executes the whole
selected block.
Installing &
Loading Packages
R packages extend the functionality of R by providing extra
functions, data, and documentation. There are over 20,000 packages, but
you only need a handful for most work. You don’t need to install
everything in advance — just install packages as you need them.
Install packages (need quotes because it’s a
string). You only need to do this once, so these
commands are shown but not run when knitting:
install.packages("ggplot2")
install.packages("here")
install.packages("tidyverse")
install.packages("readxl")
install.packages("gapminder")
install.packages("forcats")
Load packages (no quotes needed — the package name
is treated as a symbol). Do this every time you start a
new session:
library(ggplot2)
library(gapminder)
Remember: - Install only once. No need to reinstall each time you
open R. - Load every time you start a new project or session.
The here, tidyverse, readxl,
and forcats packages are also useful for managing file
paths, data manipulation, reading Excel files, and working with
categorical (factor) variables.
Working Directory
Setup
Always set your working directory first. This tells R where to look
for files and save outputs.
# What is your current working directory?
getwd()
# Set it to the folder that holds your data files:
setwd("path/to/your/Lab1")
getwd() # double check
In the live session we set the working directory with
setwd(...). In this document the data files sit next to the
.Rmd, so no setwd() is needed.
Basic R Commands
You can use R as a calculator. Be careful: R is
case sensitive — a and A are
treated as different variables (so blue, Blue,
and BLUE are three different names).
#> [1] 3
#> [1] 4
#> [1] 4
#> [1] 6
#> [1] 12
#> [1] 4
#> [1] 7
Vectors are the most basic data structure in R: an
ordered list of values (order matters!).
x <- c(1, 3, 5)
y <- c("hello", "puppy") # character
x[2]
#> [1] 3
#> [1] "puppy"
Functions.
#> [1] 3
#> [1] 2
Use ?command (or help(command)) to get help
in RStudio:
Working with Data
Frames
Loading a
dataset
.rda files are the R-native format (rda
stands for RData). Read them with load(), which puts the
dataset into your Environment under its original name.
load("EG01-19COLLEGE.rda")
# rename dataset: newname <- oldname
mydata <- `EG01-19COLLEGE`
.csv files are spreadsheets saved as
plain text with comma-separated values — the most common data format in
data science. Read them with read.csv().
df <- read.csv("EG01-19COLLEGE.csv", header = TRUE)
# header = TRUE: treat the first row as column names instead of data values
Simple way to load a dataset in RStudio: File → Open
File → click the .RData or .csv file.
# remove datasets from memory
rm(`EG01-19COLLEGE`)
rm(mydata)
Explore the data
frame
In RStudio you would run View(df); here we preview the
first rows instead.
knitr::kable(head(df), caption = "First rows of the College data")
First rows of the College data
| New Jersey |
326358 |
8640218 |
37.77196 |
| Nevada |
100760 |
2484196 |
40.56041 |
| Alaska |
27463 |
676301 |
40.60766 |
| Georgia |
378947 |
9318715 |
40.66516 |
| Connecticut |
142926 |
3487896 |
40.97771 |
| Tennessee |
250974 |
6068306 |
41.35816 |
Structure & preview.
str(df) # structure: types + sample values
#> 'data.frame': 50 obs. of 4 variables:
#> $ State : chr "New Jersey" "Nevada" "Alaska" "Georgia" ...
#> $ Undergrads : int 326358 100760 27463 378947 142926 250974 775171 187254 58512 57527 ...
#> $ Population : int 8640218 2484196 676301 9318715 3487896 6068306 18019093 4324799 1313355 1275264 ...
#> $ UGradPerThou: num 37.8 40.6 40.6 40.7 41 ...
head(df) # first six rows
df$State # access a column by name
#> [1] "New Jersey" "Nevada" "Alaska" "Georgia"
#> [5] "Connecticut" "Tennessee" "Florida" "South Carolina"
#> [9] "Maine" "Hawaii" "New Hampshire" "Montana"
#> [13] "Maryland" "Louisiana" "Oregon" "Mississippi"
#> [17] "Ohio" "Arkansas" "Pennsylvania" "Texas"
#> [21] "New York" "Alabama" "West Virginia" "Idaho"
#> [25] "North Carolina" "Washington" "Delaware" "Indiana"
#> [29] "Virginia" "Oklahoma" "Massachusetts" "Kentucky"
#> [33] "Missouri" "Colorado" "Illinois" "Wisconsin"
#> [37] "Michigan" "South Dakota" "Minnesota" "Vermont"
#> [41] "New Mexico" "Nebraska" "California" "Wyoming"
#> [45] "Kansas" "Rhode Island" "North Dakota" "Utah"
#> [49] "Iowa" "Arizona"
Size / dimension.
#> [1] 50 4
nrow(df) # number of rows
#> [1] 50
ncol(df) # number of columns
#> [1] 4
Names.
names(df) # variable names
#> [1] "State" "Undergrads" "Population" "UGradPerThou"
colnames(df) # same as names()
#> [1] "State" "Undergrads" "Population" "UGradPerThou"
rownames(df) # row names (rarely needed)
#> [1] "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "13" "14" "15"
#> [16] "16" "17" "18" "19" "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30"
#> [31] "31" "32" "33" "34" "35" "36" "37" "38" "39" "40" "41" "42" "43" "44" "45"
#> [46] "46" "47" "48" "49" "50"
Descriptive statistics.
summary(df) # summary of all variables
#> State Undergrads Population UGradPerThou
#> Length:50 Min. : 27463 Min. : 512573 Min. :37.77
#> Class :character 1st Qu.: 90659 1st Qu.: 1771524 1st Qu.:45.73
#> Mode :character Median : 203641 Median : 4221537 Median :49.43
#> Mean : 302136 Mean : 5955551 Mean :51.45
#> 3rd Qu.: 333223 3rd Qu.: 6422700 3rd Qu.:54.46
#> Max. :2172354 Max. :36121296 Max. :77.13
summary(df$Population) # summary of one column
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 512573 1771524 4221537 5955551 6422700 36121296
#> [1] 5955551
#> [1] 4221537
#> [1] 512573
#> [1] 36121296
Try it. What is the mean of the undergrad population
in the dataset? What about the median? (Type your own code.)
Histograms and
Boxplots
Base R plots
Histograms (continuous variables) show the
distribution of a variable — are values concentrated, spread out, or
skewed?
hist(df$Population) # basic

hist(df$Population, breaks = 20) # adjust number of bins

hist(df$Population, main = "Population (Histogram)",
xlab = "Population", ylab = "Frequency", col = "lightgreen", breaks = 20)

Boxplots show the median, lower and upper quartiles,
and outliers.
boxplot(df$Population) # basic vertical

boxplot(df$Population, horizontal = TRUE) # horizontal style

boxplot(df$Population, horizontal = TRUE,
main = "Population (Boxplot)",
xlab = "Population", col = "purple")

Visualization with
ggplot2
Build plots layer by layer: start with data + aes() +
geoms. ggplot2 implements the grammar of
graphics — use + to add layers, and order
matters!
Scatterplot
(Gapminder dataset)
Goal: explore the relationship between GDP per capita and life
expectancy.
ggplot(data = gm, aes(x = gdpPercap, y = lifeExp)) # start with data + aes

ggplot(data = gm, aes(x = gdpPercap, y = lifeExp)) +
geom_point() # add scatter points

ggplot(data = gm, aes(x = gdpPercap, y = lifeExp)) +
geom_point() +
labs(title = "Life Expectancy vs GDP per Capita", # add title + labels
x = "GDP per Capita", y = "Life Expectancy")

ggplot(data = gm, aes(x = gdpPercap, y = lifeExp)) +
geom_point() +
labs(title = "Life Expectancy vs GDP per Capita",
x = "GDP per Capita", y = "Life Expectancy") +
geom_smooth(color = "red") # add smooth trend line

# Note: grey shading = confidence interval
Histogram (continuous
variable)
Show the distribution of Population.
ggplot(data = df, aes(x = Population)) +
geom_histogram() # basic histogram

ggplot(data = df, aes(x = Population)) +
geom_histogram(bins = 40) # adjust number of bins

ggplot(df, aes(x = Population)) +
geom_histogram(bins = 40, fill = "steelblue") + # add color
labs(title = "Population (Histogram)", x = "Population", y = "Count")

Bar chart
(categorical variable)
Load a second dataset and draw a bar chart of search sources.
load("EG01-07ONLINE.rda")
df2 <- `EG01-07ONLINE` # renaming the data frame
ggplot(df2, aes(x = Source, y = Count)) +
geom_bar(stat = "identity") # basic bar chart

ggplot(data = df2, aes(x = Source, y = Count)) +
geom_bar(stat = "identity", fill = "forestgreen") # add color

# change the order of the bars
order <- c("Library", "Google", "Wikipedia", "Other")
ggplot(data = df2, aes(x = Source, y = Count)) +
geom_bar(stat = "identity", fill = "forestgreen") +
scale_x_discrete(limits = order)

LS0tCnRpdGxlOiAiTGFiIDEg4oCUIEludHJvZHVjdGlvbiB0byBSIgpzdWJ0aXRsZTogIlF1YW50aXRhdGl2ZSBSZWFzb25pbmcgwrcgTEFCIDQxMyIKYXV0aG9yOiAiSW5zdHJ1Y3RvcjogU3ViaW4gTmEiCmRhdGU6ICJTZXB0ZW1iZXIgMywgMjAyNSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNC4yLAogIGNvbW1lbnQgPSAiIz4iCikKYGBgCgo+ICoqSG93IHRvIHVzZSB0aGlzIGRvY3VtZW50LioqIE1ha2Ugbm90ZXMgb24geW91ciBvd24hIENvbW1lbnRzIHN0YXJ0IHdpdGggYCNgCj4gKFIgaWdub3JlcyB0aGVtKS4gQWN0dWFsbHkgcnVuIHRoZSBjb2RlIGFsb25nIHdpdGggbWUuIEVhY2ggc2VjdGlvbiBtaXJyb3JzIHRoZQo+IGxpdmUgbGFiOyBjb2RlIGNodW5rcyBzaG93IHRoZSBjb21tYW5kIGFuZCBpdHMgb3V0cHV0IHRvZ2V0aGVyLgoKKipUb2RheSdzIHBsYW4qKgoKMS4gR2V0dGluZyBzdGFydGVkIHdpdGggUlN0dWRpbwoyLiBJbnN0YWxsaW5nICYgbG9hZGluZyBwYWNrYWdlcwozLiBXb3JraW5nIGRpcmVjdG9yeSBzZXR1cAo0LiBCYXNpYyBSIGNvbW1hbmRzCjUuIFdvcmtpbmcgd2l0aCBkYXRhIGZyYW1lcwo2LiBIaXN0b2dyYW1zIGFuZCBib3hwbG90cwo3LiBWaXN1YWxpemF0aW9uIHdpdGggYGdncGxvdDJgCgotLS0KCiMgR2V0dGluZyBTdGFydGVkIHdpdGggUlN0dWRpbwoKUlN0dWRpbyBpcyBvcmdhbml6ZWQgaW50byAqKmZvdXIgcGFuZXMqKjogU291cmNlIHwgQ29uc29sZSB8IEVudmlyb25tZW50IHwKRmlsZXMvUGxvdHMvUGFja2FnZXMvSGVscC4KCj4gKipUaXAuKiogQ3VzdG9taXplIFJTdHVkaW8gdW5kZXIgKipUb29scyDihpIgR2xvYmFsIE9wdGlvbnMg4oaSIEFwcGVhcmFuY2UgLyBQYW5lCj4gTGF5b3V0KiouCgoqKlJ1bm5pbmcgY29kZS4qKiBDbGljayB0aGUgKipSdW4qKiBidXR0b24sIG9yIHVzZSB0aGUga2V5Ym9hcmQgc2hvcnRjdXRzOgoKLSBNYWM6IGBjbWQgKyByZXR1cm5gCi0gV2luZG93czogYGN0cmwgKyBlbnRlcmAKLSBJZiB5b3VyIGN1cnNvciBpcyBhbnl3aGVyZSBvbiBhIHNpbmdsZSBsaW5lLCBSU3R1ZGlvIGV4ZWN1dGVzIGp1c3QgdGhhdCBsaW5lLgotIElmIHlvdSBoaWdobGlnaHQgKHNlbGVjdCkgbXVsdGlwbGUgbGluZXMsIFJTdHVkaW8gZXhlY3V0ZXMgdGhlIHdob2xlIHNlbGVjdGVkIGJsb2NrLgoKIyBJbnN0YWxsaW5nICYgTG9hZGluZyBQYWNrYWdlcwoKUiBwYWNrYWdlcyBleHRlbmQgdGhlIGZ1bmN0aW9uYWxpdHkgb2YgUiBieSBwcm92aWRpbmcgZXh0cmEgZnVuY3Rpb25zLCBkYXRhLCBhbmQKZG9jdW1lbnRhdGlvbi4gVGhlcmUgYXJlIG92ZXIgMjAsMDAwIHBhY2thZ2VzLCBidXQgeW91IG9ubHkgbmVlZCBhIGhhbmRmdWwgZm9yCm1vc3Qgd29yay4gWW91IGRvbid0IG5lZWQgdG8gaW5zdGFsbCBldmVyeXRoaW5nIGluIGFkdmFuY2Ug4oCUIGp1c3QgaW5zdGFsbApwYWNrYWdlcyBhcyB5b3UgbmVlZCB0aGVtLgoKKipJbnN0YWxsIHBhY2thZ2VzKiogKG5lZWQgcXVvdGVzIGJlY2F1c2UgaXQncyBhIHN0cmluZykuIFlvdSBvbmx5IG5lZWQgdG8gZG8KdGhpcyAqKm9uY2UqKiwgc28gdGhlc2UgY29tbWFuZHMgYXJlIHNob3duIGJ1dCBub3QgcnVuIHdoZW4ga25pdHRpbmc6CgpgYGB7ciBpbnN0YWxsLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKaW5zdGFsbC5wYWNrYWdlcygiaGVyZSIpCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpCmluc3RhbGwucGFja2FnZXMoImdhcG1pbmRlciIpCmluc3RhbGwucGFja2FnZXMoImZvcmNhdHMiKQpgYGAKCioqTG9hZCBwYWNrYWdlcyoqIChubyBxdW90ZXMgbmVlZGVkIOKAlCB0aGUgcGFja2FnZSBuYW1lIGlzIHRyZWF0ZWQgYXMgYSBzeW1ib2wpLgpEbyB0aGlzICoqZXZlcnkgdGltZSoqIHlvdSBzdGFydCBhIG5ldyBzZXNzaW9uOgoKYGBge3IgbG9hZC1wa2dzfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2FwbWluZGVyKQpgYGAKCj4gUmVtZW1iZXI6Cj4gLSBJbnN0YWxsIG9ubHkgb25jZS4gTm8gbmVlZCB0byByZWluc3RhbGwgZWFjaCB0aW1lIHlvdSBvcGVuIFIuCj4gLSBMb2FkIGV2ZXJ5IHRpbWUgeW91IHN0YXJ0IGEgbmV3IHByb2plY3Qgb3Igc2Vzc2lvbi4KPgo+IFRoZSBgaGVyZWAsIGB0aWR5dmVyc2VgLCBgcmVhZHhsYCwgYW5kIGBmb3JjYXRzYCBwYWNrYWdlcyBhcmUgYWxzbyB1c2VmdWwgZm9yCj4gbWFuYWdpbmcgZmlsZSBwYXRocywgZGF0YSBtYW5pcHVsYXRpb24sIHJlYWRpbmcgRXhjZWwgZmlsZXMsIGFuZCB3b3JraW5nIHdpdGgKPiBjYXRlZ29yaWNhbCAoZmFjdG9yKSB2YXJpYWJsZXMuCgojIFdvcmtpbmcgRGlyZWN0b3J5IFNldHVwCgpBbHdheXMgc2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkgZmlyc3QuIFRoaXMgdGVsbHMgUiB3aGVyZSB0byBsb29rIGZvciBmaWxlcyBhbmQKc2F2ZSBvdXRwdXRzLgoKYGBge3IgZ2V0d2QsIGV2YWw9RkFMU0V9CiMgV2hhdCBpcyB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3Rvcnk/CmdldHdkKCkKCiMgU2V0IGl0IHRvIHRoZSBmb2xkZXIgdGhhdCBob2xkcyB5b3VyIGRhdGEgZmlsZXM6CnNldHdkKCJwYXRoL3RvL3lvdXIvTGFiMSIpCmdldHdkKCkgICAjIGRvdWJsZSBjaGVjawpgYGAKCj4gSW4gdGhlIGxpdmUgc2Vzc2lvbiB3ZSBzZXQgdGhlIHdvcmtpbmcgZGlyZWN0b3J5IHdpdGggYHNldHdkKC4uLilgLiBJbiB0aGlzCj4gZG9jdW1lbnQgdGhlIGRhdGEgZmlsZXMgc2l0IG5leHQgdG8gdGhlIGAuUm1kYCwgc28gbm8gYHNldHdkKClgIGlzIG5lZWRlZC4KCiMgQmFzaWMgUiBDb21tYW5kcwoKWW91IGNhbiB1c2UgUiBhcyBhICoqY2FsY3VsYXRvcioqLiBCZSBjYXJlZnVsOiBSIGlzICoqY2FzZSBzZW5zaXRpdmUqKiDigJQgYGFgIGFuZApgQWAgYXJlIHRyZWF0ZWQgYXMgZGlmZmVyZW50IHZhcmlhYmxlcyAoc28gYGJsdWVgLCBgQmx1ZWAsIGFuZCBgQkxVRWAgYXJlIHRocmVlCmRpZmZlcmVudCBuYW1lcykuCgpgYGB7ciBhc3NpZ259CmEgPC0gMwpBIDwtIDQKYQpBCmBgYAoKYGBge3IgYXJpdGhtZXRpY30KMiArIDIKMTAgLSA0CjMgKiA0CjEyIC8gMwoKYSArIEEKYGBgCgoqKlZlY3RvcnMqKiBhcmUgdGhlIG1vc3QgYmFzaWMgZGF0YSBzdHJ1Y3R1cmUgaW4gUjogYW4gb3JkZXJlZCBsaXN0IG9mIHZhbHVlcwoob3JkZXIgbWF0dGVycyEpLgoKYGBge3IgdmVjdG9yc30KeCA8LSBjKDEsIDMsIDUpCnkgPC0gYygiaGVsbG8iLCAicHVwcHkiKSAgICMgY2hhcmFjdGVyCgp4WzJdCnlbMl0KYGBgCgoqKkZ1bmN0aW9ucy4qKgoKYGBge3IgZnVuY3Rpb25zfQptZWFuKHgpCnNxcnQoQSkKYGBgCgpVc2UgYD9jb21tYW5kYCAob3IgYGhlbHAoY29tbWFuZClgKSB0byBnZXQgaGVscCBpbiBSU3R1ZGlvOgoKYGBge3IgaGVscCwgZXZhbD1GQUxTRX0KP21lYW4KaGVscChzcXJ0KQpgYGAKCiMgV29ya2luZyB3aXRoIERhdGEgRnJhbWVzCgojIyBMb2FkaW5nIGEgZGF0YXNldAoKKipgLnJkYWAgZmlsZXMqKiBhcmUgdGhlIFItbmF0aXZlIGZvcm1hdCAocmRhIHN0YW5kcyBmb3IgUkRhdGEpLiBSZWFkIHRoZW0gd2l0aApgbG9hZCgpYCwgd2hpY2ggcHV0cyB0aGUgZGF0YXNldCBpbnRvIHlvdXIgRW52aXJvbm1lbnQgdW5kZXIgaXRzIG9yaWdpbmFsIG5hbWUuCgpgYGB7ciBsb2FkLXJkYX0KbG9hZCgiRUcwMS0xOUNPTExFR0UucmRhIikKCiMgcmVuYW1lIGRhdGFzZXQ6IG5ld25hbWUgPC0gb2xkbmFtZQpteWRhdGEgPC0gYEVHMDEtMTlDT0xMRUdFYApgYGAKCioqYC5jc3ZgIGZpbGVzKiogYXJlIHNwcmVhZHNoZWV0cyBzYXZlZCBhcyBwbGFpbiB0ZXh0IHdpdGggY29tbWEtc2VwYXJhdGVkCnZhbHVlcyDigJQgdGhlIG1vc3QgY29tbW9uIGRhdGEgZm9ybWF0IGluIGRhdGEgc2NpZW5jZS4gUmVhZCB0aGVtIHdpdGgKYHJlYWQuY3N2KClgLgoKYGBge3IgcmVhZC1jc3Z9CmRmIDwtIHJlYWQuY3N2KCJFRzAxLTE5Q09MTEVHRS5jc3YiLCBoZWFkZXIgPSBUUlVFKQojIGhlYWRlciA9IFRSVUU6IHRyZWF0IHRoZSBmaXJzdCByb3cgYXMgY29sdW1uIG5hbWVzIGluc3RlYWQgb2YgZGF0YSB2YWx1ZXMKYGBgCgo+ICoqU2ltcGxlIHdheSB0byBsb2FkIGEgZGF0YXNldCBpbiBSU3R1ZGlvOioqIEZpbGUg4oaSIE9wZW4gRmlsZSDihpIgY2xpY2sgdGhlCj4gYC5SRGF0YWAgb3IgYC5jc3ZgIGZpbGUuCgpgYGB7ciBybX0KIyByZW1vdmUgZGF0YXNldHMgZnJvbSBtZW1vcnkKcm0oYEVHMDEtMTlDT0xMRUdFYCkKcm0obXlkYXRhKQpgYGAKCiMjIEV4cGxvcmUgdGhlIGRhdGEgZnJhbWUKCkluIFJTdHVkaW8geW91IHdvdWxkIHJ1biBgVmlldyhkZilgOyBoZXJlIHdlIHByZXZpZXcgdGhlIGZpcnN0IHJvd3MgaW5zdGVhZC4KCmBgYHtyIHZpZXd9CmtuaXRyOjprYWJsZShoZWFkKGRmKSwgY2FwdGlvbiA9ICJGaXJzdCByb3dzIG9mIHRoZSBDb2xsZWdlIGRhdGEiKQpgYGAKCioqU3RydWN0dXJlICYgcHJldmlldy4qKgoKYGBge3Igc3RydWN0dXJlfQpzdHIoZGYpICAgICAgICAjIHN0cnVjdHVyZTogdHlwZXMgKyBzYW1wbGUgdmFsdWVzCmhlYWQoZGYpICAgICAgICMgZmlyc3Qgc2l4IHJvd3MKZGYkU3RhdGUgICAgICAgIyBhY2Nlc3MgYSBjb2x1bW4gYnkgbmFtZQpgYGAKCioqU2l6ZSAvIGRpbWVuc2lvbi4qKgoKYGBge3IgZGltc30KZGltKGRmKSAgICAgICAgIyByb3dzIHggY29sdW1ucwpucm93KGRmKSAgICAgICAjIG51bWJlciBvZiByb3dzCm5jb2woZGYpICAgICAgICMgbnVtYmVyIG9mIGNvbHVtbnMKYGBgCgoqKk5hbWVzLioqCgpgYGB7ciBuYW1lc30KbmFtZXMoZGYpICAgICAgIyB2YXJpYWJsZSBuYW1lcwpjb2xuYW1lcyhkZikgICAjIHNhbWUgYXMgbmFtZXMoKQpyb3duYW1lcyhkZikgICAjIHJvdyBuYW1lcyAocmFyZWx5IG5lZWRlZCkKYGBgCgoqKkRlc2NyaXB0aXZlIHN0YXRpc3RpY3MuKioKCmBgYHtyIHN1bW1hcnl9CnN1bW1hcnkoZGYpICAgICAgICAgICAgIyBzdW1tYXJ5IG9mIGFsbCB2YXJpYWJsZXMKc3VtbWFyeShkZiRQb3B1bGF0aW9uKSAjIHN1bW1hcnkgb2Ygb25lIGNvbHVtbgoKbWVhbihkZiRQb3B1bGF0aW9uKQptZWRpYW4oZGYkUG9wdWxhdGlvbikKbWluKGRmJFBvcHVsYXRpb24pCm1heChkZiRQb3B1bGF0aW9uKQpgYGAKCj4gKipUcnkgaXQuKiogV2hhdCBpcyB0aGUgbWVhbiBvZiB0aGUgdW5kZXJncmFkIHBvcHVsYXRpb24gaW4gdGhlIGRhdGFzZXQ/IFdoYXQKPiBhYm91dCB0aGUgbWVkaWFuPyAoVHlwZSB5b3VyIG93biBjb2RlLikKCiMgSGlzdG9ncmFtcyBhbmQgQm94cGxvdHMKCiMjIEJhc2UgUiBwbG90cwoKKipIaXN0b2dyYW1zKiogKGNvbnRpbnVvdXMgdmFyaWFibGVzKSBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgYSB2YXJpYWJsZSDigJQgYXJlCnZhbHVlcyBjb25jZW50cmF0ZWQsIHNwcmVhZCBvdXQsIG9yIHNrZXdlZD8KCmBgYHtyIGhpc3QtYmFzaWN9Cmhpc3QoZGYkUG9wdWxhdGlvbikgICAgICAgICAgICAgICAgICAgICAjIGJhc2ljCmBgYAoKYGBge3IgaGlzdC1iaW5zfQpoaXN0KGRmJFBvcHVsYXRpb24sIGJyZWFrcyA9IDIwKSAgICAgICAgIyBhZGp1c3QgbnVtYmVyIG9mIGJpbnMKYGBgCgpgYGB7ciBoaXN0LWxhYmVsZWR9Cmhpc3QoZGYkUG9wdWxhdGlvbiwgbWFpbiA9ICJQb3B1bGF0aW9uIChIaXN0b2dyYW0pIiwKICAgICB4bGFiID0gIlBvcHVsYXRpb24iLCB5bGFiID0gIkZyZXF1ZW5jeSIsIGNvbCA9ICJsaWdodGdyZWVuIiwgYnJlYWtzID0gMjApCmBgYAoKKipCb3hwbG90cyoqIHNob3cgdGhlIG1lZGlhbiwgbG93ZXIgYW5kIHVwcGVyIHF1YXJ0aWxlcywgYW5kIG91dGxpZXJzLgoKYGBge3IgYm94LWJhc2ljfQpib3hwbG90KGRmJFBvcHVsYXRpb24pICAgICAgICAgICAgICAgICAgIyBiYXNpYyB2ZXJ0aWNhbApgYGAKCmBgYHtyIGJveC1ob3Jpem9udGFsfQpib3hwbG90KGRmJFBvcHVsYXRpb24sIGhvcml6b250YWwgPSBUUlVFKSAgICMgaG9yaXpvbnRhbCBzdHlsZQpgYGAKCmBgYHtyIGJveC1sYWJlbGVkfQpib3hwbG90KGRmJFBvcHVsYXRpb24sIGhvcml6b250YWwgPSBUUlVFLAogICAgICAgIG1haW4gPSAiUG9wdWxhdGlvbiAoQm94cGxvdCkiLAogICAgICAgIHhsYWIgPSAiUG9wdWxhdGlvbiIsIGNvbCA9ICJwdXJwbGUiKQpgYGAKCiMgVmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDIKCkJ1aWxkIHBsb3RzIGxheWVyIGJ5IGxheWVyOiBzdGFydCB3aXRoIGRhdGEgKyBgYWVzKClgICsgZ2VvbXMuIGBnZ3Bsb3QyYAppbXBsZW1lbnRzIHRoZSAqKmdyYW1tYXIgb2YgZ3JhcGhpY3MqKiDigJQgdXNlIGArYCB0byBhZGQgbGF5ZXJzLCBhbmQgb3JkZXIKbWF0dGVycyEKCiMjIFNjYXR0ZXJwbG90IChHYXBtaW5kZXIgZGF0YXNldCkKCkdvYWw6IGV4cGxvcmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEdEUCBwZXIgY2FwaXRhIGFuZCBsaWZlIGV4cGVjdGFuY3kuCgpgYGB7ciBnbX0KZ20gPC0gZ2FwbWluZGVyCmBgYAoKYGBge3IgZ20tYWVzfQpnZ3Bsb3QoZGF0YSA9IGdtLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKSAgICAgICAgICAjIHN0YXJ0IHdpdGggZGF0YSArIGFlcwpgYGAKCmBgYHtyIGdtLXBvaW50c30KZ2dwbG90KGRhdGEgPSBnbSwgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwKSkgKwogIGdlb21fcG9pbnQoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHNjYXR0ZXIgcG9pbnRzCmBgYAoKYGBge3IgZ20tbGFic30KZ2dwbG90KGRhdGEgPSBnbSwgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJMaWZlIEV4cGVjdGFuY3kgdnMgR0RQIHBlciBDYXBpdGEiLCAgIyBhZGQgdGl0bGUgKyBsYWJlbHMKICAgICAgIHggPSAiR0RQIHBlciBDYXBpdGEiLCB5ID0gIkxpZmUgRXhwZWN0YW5jeSIpCmBgYAoKYGBge3IgZ20tc21vb3RofQpnZ3Bsb3QoZGF0YSA9IGdtLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKSArCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHRpdGxlID0gIkxpZmUgRXhwZWN0YW5jeSB2cyBHRFAgcGVyIENhcGl0YSIsCiAgICAgICB4ID0gIkdEUCBwZXIgQ2FwaXRhIiwgeSA9ICJMaWZlIEV4cGVjdGFuY3kiKSArCiAgZ2VvbV9zbW9vdGgoY29sb3IgPSAicmVkIikgICAgICAgICAgICAgICAgICAgICAgICAjIGFkZCBzbW9vdGggdHJlbmQgbGluZQojIE5vdGU6IGdyZXkgc2hhZGluZyA9IGNvbmZpZGVuY2UgaW50ZXJ2YWwKYGBgCgojIyBIaXN0b2dyYW0gKGNvbnRpbnVvdXMgdmFyaWFibGUpCgpTaG93IHRoZSBkaXN0cmlidXRpb24gb2YgYFBvcHVsYXRpb25gLgoKYGBge3IgZ2ctaGlzdC1iYXNpY30KZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBQb3B1bGF0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYmFzaWMgaGlzdG9ncmFtCmBgYAoKYGBge3IgZ2ctaGlzdC1iaW5zfQpnZ3Bsb3QoZGF0YSA9IGRmLCBhZXMoeCA9IFBvcHVsYXRpb24pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDQwKSAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhZGp1c3QgbnVtYmVyIG9mIGJpbnMKYGBgCgpgYGB7ciBnZy1oaXN0LWNvbG9yfQpnZ3Bsb3QoZGYsIGFlcyh4ID0gUG9wdWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDAsIGZpbGwgPSAic3RlZWxibHVlIikgKyAgICAjIGFkZCBjb2xvcgogIGxhYnModGl0bGUgPSAiUG9wdWxhdGlvbiAoSGlzdG9ncmFtKSIsIHggPSAiUG9wdWxhdGlvbiIsIHkgPSAiQ291bnQiKQpgYGAKCiMjIEJhciBjaGFydCAoY2F0ZWdvcmljYWwgdmFyaWFibGUpCgpMb2FkIGEgc2Vjb25kIGRhdGFzZXQgYW5kIGRyYXcgYSBiYXIgY2hhcnQgb2Ygc2VhcmNoIHNvdXJjZXMuCgpgYGB7ciBsb2FkLW9ubGluZX0KbG9hZCgiRUcwMS0wN09OTElORS5yZGEiKQpkZjIgPC0gYEVHMDEtMDdPTkxJTkVgICAgIyByZW5hbWluZyB0aGUgZGF0YSBmcmFtZQpgYGAKCmBgYHtyIGJhci1iYXNpY30KZ2dwbG90KGRmMiwgYWVzKHggPSBTb3VyY2UsIHkgPSBDb3VudCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgICAjIGJhc2ljIGJhciBjaGFydApgYGAKCmBgYHtyIGJhci1jb2xvcn0KZ2dwbG90KGRhdGEgPSBkZjIsIGFlcyh4ID0gU291cmNlLCB5ID0gQ291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiZm9yZXN0Z3JlZW4iKSAgICMgYWRkIGNvbG9yCmBgYAoKYGBge3IgYmFyLW9yZGVyfQojIGNoYW5nZSB0aGUgb3JkZXIgb2YgdGhlIGJhcnMKb3JkZXIgPC0gYygiTGlicmFyeSIsICJHb29nbGUiLCAiV2lraXBlZGlhIiwgIk90aGVyIikKCmdncGxvdChkYXRhID0gZGYyLCBhZXMoeCA9IFNvdXJjZSwgeSA9IENvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImZvcmVzdGdyZWVuIikgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gb3JkZXIpCmBgYAo=