How to use this document. Run the code along with
me. Comments start with #. Each section mirrors the live
lab; code chunks show the command and its output together.
Today’s plan
- Quick reminders: new R session setup
- Working with data frames (review from Lab 1)
- Histograms and boxplots (Base R)
- Visualization with
ggplot2
- Variable transformation
- Correlation
- Linear regression
Quick Reminders: New R
Session Setup
Always follow these steps when you open RStudio:
- Install packages (only once on your computer).
- Load packages (every new session).
- Set your working directory.
- Import your dataset.
# install.packages("tidyverse") # only once
# install.packages("dplyr")
# Load packages
library(tidyverse)
library(dplyr)
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.
Working with Data
Frames
Loading a
Dataset
.rda files are R’s 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")
# Best practice: rename it to a simpler name you can use easily.
# newname <- `oldname` (wrap the original name in backticks)
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
A simple way to load data in RStudio: File → Open File
→ click the .RData or .csv file.
# Remove datasets we no longer need 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 |
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, 10) # first ten 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"
#> [1] 50 4
nrow(df) # number of rows
#> [1] 50
ncol(df) # number of columns
#> [1] 4
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
mean(df$Population) # use a column, NOT mean(df)
#> [1] 5955551
#> [1] 4221537
#> [1] 512573
#> [1] 36121296
TODO (descriptive statistics). Using
EG01-19COLLEGE.csv:
- Calculate the mean of the
Undergrads column with
mean().
- Calculate the median of the
Undergrads column with
median().
#> [1] 302135.9
#> [1] 203641
Histograms and Boxplots
(Base R)
Histograms
(continuous variables)
Histograms 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, breaks = 100)

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

Boxplots (quartiles
and outliers)
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 is a grammar of
graphics. Remember: (1) use + to add layers, and (2)
the order matters.
Scatterplot
(Gapminder data)
Goal: explore the relationship between GDP per capita and life
expectancy.
library(gapminder)
gm <- gapminder
names(gm)
#> [1] "country" "continent" "year" "lifeExp" "pop" "gdpPercap"
ggplot(gm, aes(x = gdpPercap, y = lifeExp)) # base layer only

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",
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 = "pink") # smooth trend line

# Note: grey shading = confidence interval
Histogram (continuous
variable)
Show the distribution of Population from
EG01-19COLLEGE.csv.
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") +
labs(title = "Population (Histogram)", x = "Population", y = "Count")

Bar Chart
(categorical variable)
Goal: draw a bar chart using the EG01-07ONLINE.rda
dataset.
load("EG01-07ONLINE.rda")
df2 <- `EG01-07ONLINE` # rename 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") +
labs(title = "Source (Bar Chart)", x = "Source", y = "Counts")

# 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") +
labs(title = "Source (Bar Chart)", x = "Source", y = "Counts") +
scale_x_discrete(limits = order)

TODO (visualization). Using the gapminder data, draw
a bar chart of continent (x) vs. lifeExp (y).
Pick your favorite fill color, add a title and axis labels
with labs(), and reorder the continents as: Americas, Asia,
Africa, Oceania, Europe.
order2 <- c("Americas", "Asia", "Africa", "Oceania", "Europe")
ggplot(data = gm, aes(x = continent, y = lifeExp)) +
geom_bar(stat = "identity", fill = "brown") +
labs(title = "Continent vs. Life Expectancy",
x = "Continent", y = "Life Expectancy") +
scale_x_discrete(limits = order2)

Correlation
Correlation measures how two variables move together.
# Scatterplot: SAT Writing vs. SAT Critical Reading
ggplot(data = gpa, aes(x = SATW, y = SATCR)) +
geom_point()

# Correlation coefficient
cor(gpa$SATW, gpa$SATCR, use = "complete.obs")
#> [1] 0.7343451
A common threshold for high correlation is 0.7 or
above.
TODO (correlation). Draw a scatterplot of
GPA vs. HSM (high-school math grades) and
calculate the correlation coefficient.
ggplot(data = gpa, aes(x = GPA, y = HSM)) +
geom_point()

cor(gpa$GPA, gpa$HSM, use = "complete.obs")
#> [1] 0.4197389
Linear Regression
Regression is about predicting a relationship — drawing the best-fit
line through the data. We estimate it with
lm(y ~ x, data = ).
# Predict SAT Critical Reading from SAT Writing
m1 <- lm(SATCR ~ SATW, data = gpa)
# Note: use a new name for each model, or it will overwrite the old one.
summary(m1)
#>
#> Call:
#> lm(formula = SATCR ~ SATW, data = gpa)
#>
#> Residuals:
#> Min 1Q Median 3Q Max
#> -225.643 -35.408 0.169 39.678 152.563
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 121.79546 34.68694 3.511 0.000591 ***
#> SATW 0.80342 0.06104 13.161 < 2e-16 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 59.68 on 148 degrees of freedom
#> Multiple R-squared: 0.5393, Adjusted R-squared: 0.5361
#> F-statistic: 173.2 on 1 and 148 DF, p-value: < 2.2e-16
# Scatterplot + regression line
ggplot(gpa, aes(x = SATW, y = SATCR)) +
geom_point() +
geom_smooth(method = lm, color = "red")

# Gray shaded area: confidence interval (the margin of uncertainty)
TODO (regression). Predict SATCR from
SATM, interpret the output, then plot the scatterplot with
the regression line added.
m2 <- lm(SATCR ~ SATM, data = gpa)
summary(m2)
#>
#> Call:
#> lm(formula = SATCR ~ SATM, data = gpa)
#>
#> Residuals:
#> Min 1Q Median 3Q Max
#> -275.264 -38.826 -6.939 46.023 202.889
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 150.93431 49.27543 3.063 0.0026 **
#> SATM 0.67810 0.07846 8.643 8.24e-15 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 71.67 on 148 degrees of freedom
#> Multiple R-squared: 0.3354, Adjusted R-squared: 0.3309
#> F-statistic: 74.7 on 1 and 148 DF, p-value: 8.241e-15
ggplot(gpa, aes(x = SATM, y = SATCR)) +
geom_point() +
geom_smooth(method = lm, color = "red")

LS0tCnRpdGxlOiAiTGFiIDIg4oCUIFZpc3VhbGl6YXRpb24sIFRyYW5zZm9ybWF0aW9uLCBDb3JyZWxhdGlvbiAmIFJlZ3Jlc3Npb24iCnN1YnRpdGxlOiAiUXVhbnRpdGF0aXZlIFJlYXNvbmluZyDCtyBMQUIgNDEzIgphdXRob3I6ICJJbnN0cnVjdG9yOiBTdWJpbiBOYSIKZGF0ZTogIlNlcHRlbWJlciAxMCwgMjAyNSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNC4yLAogIGNvbW1lbnQgPSAiIz4iCikKYGBgCgo+ICoqSG93IHRvIHVzZSB0aGlzIGRvY3VtZW50LioqIFJ1biB0aGUgY29kZSBhbG9uZyB3aXRoIG1lLiBDb21tZW50cyBzdGFydCB3aXRoIGAjYC4KPiBFYWNoIHNlY3Rpb24gbWlycm9ycyB0aGUgbGl2ZSBsYWI7IGNvZGUgY2h1bmtzIHNob3cgdGhlIGNvbW1hbmQgYW5kIGl0cyBvdXRwdXQgdG9nZXRoZXIuCgoqKlRvZGF5J3MgcGxhbioqCgoxLiBRdWljayByZW1pbmRlcnM6IG5ldyBSIHNlc3Npb24gc2V0dXAKMi4gV29ya2luZyB3aXRoIGRhdGEgZnJhbWVzIChyZXZpZXcgZnJvbSBMYWIgMSkKMy4gSGlzdG9ncmFtcyBhbmQgYm94cGxvdHMgKEJhc2UgUikKNC4gVmlzdWFsaXphdGlvbiB3aXRoIGBnZ3Bsb3QyYAo1LiBWYXJpYWJsZSB0cmFuc2Zvcm1hdGlvbgo2LiBDb3JyZWxhdGlvbgo3LiBMaW5lYXIgcmVncmVzc2lvbgoKLS0tCgojIFF1aWNrIFJlbWluZGVyczogTmV3IFIgU2Vzc2lvbiBTZXR1cAoKQWx3YXlzIGZvbGxvdyB0aGVzZSBzdGVwcyB3aGVuIHlvdSBvcGVuIFJTdHVkaW86CgoxLiAqKkluc3RhbGwqKiBwYWNrYWdlcyAob25seSBvbmNlIG9uIHlvdXIgY29tcHV0ZXIpLgoyLiAqKkxvYWQqKiBwYWNrYWdlcyAoZXZlcnkgbmV3IHNlc3Npb24pLgozLiAqKlNldCoqIHlvdXIgd29ya2luZyBkaXJlY3RvcnkuCjQuICoqSW1wb3J0KiogeW91ciBkYXRhc2V0LgoKYGBge3IgcGFja2FnZXN9CiMgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikgICAjIG9ubHkgb25jZQojIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKCiMgTG9hZCBwYWNrYWdlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkcGx5cikKYGBgCgo+IEluIHRoZSBsaXZlIHNlc3Npb24gd2Ugc2V0IHRoZSB3b3JraW5nIGRpcmVjdG9yeSB3aXRoIGBzZXR3ZCguLi4pYC4gSW4gdGhpcwo+IGRvY3VtZW50IHRoZSBkYXRhIGZpbGVzIHNpdCBuZXh0IHRvIHRoZSBgLlJtZGAsIHNvIG5vIGBzZXR3ZCgpYCBpcyBuZWVkZWQuCgojIFdvcmtpbmcgd2l0aCBEYXRhIEZyYW1lcwoKIyMgTG9hZGluZyBhIERhdGFzZXQKCioqYC5yZGFgIGZpbGVzKiogYXJlIFIncyBuYXRpdmUgZm9ybWF0IChyZGEgc3RhbmRzIGZvciBSRGF0YSkuIFJlYWQgdGhlbSB3aXRoCmBsb2FkKClgLCB3aGljaCBwdXRzIHRoZSBkYXRhc2V0IGludG8geW91ciBlbnZpcm9ubWVudCB1bmRlciBpdHMgb3JpZ2luYWwgbmFtZS4KCmBgYHtyIGxvYWQtcmRhfQpsb2FkKCJFRzAxLTE5Q09MTEVHRS5yZGEiKQoKIyBCZXN0IHByYWN0aWNlOiByZW5hbWUgaXQgdG8gYSBzaW1wbGVyIG5hbWUgeW91IGNhbiB1c2UgZWFzaWx5LgojIG5ld25hbWUgPC0gYG9sZG5hbWVgICAod3JhcCB0aGUgb3JpZ2luYWwgbmFtZSBpbiBiYWNrdGlja3MpCm15ZGF0YSA8LSBgRUcwMS0xOUNPTExFR0VgCmBgYAoKKipgLmNzdmAgZmlsZXMqKiBhcmUgc3ByZWFkc2hlZXRzIHNhdmVkIGFzIHBsYWluIHRleHQgd2l0aCBjb21tYS1zZXBhcmF0ZWQKdmFsdWVzIOKAlCB0aGUgbW9zdCBjb21tb24gZGF0YSBmb3JtYXQgaW4gZGF0YSBzY2llbmNlLiBSZWFkIHRoZW0gd2l0aCBgcmVhZC5jc3YoKWAuCgpgYGB7ciBsb2FkLWNzdn0KZGYgPC0gcmVhZC5jc3YoIkVHMDEtMTlDT0xMRUdFLmNzdiIsIGhlYWRlciA9IFRSVUUpCiMgaGVhZGVyID0gVFJVRTogdHJlYXQgdGhlIGZpcnN0IHJvdyBhcyBjb2x1bW4gbmFtZXMgaW5zdGVhZCBvZiBkYXRhIHZhbHVlcwpgYGAKCj4gQSBzaW1wbGUgd2F5IHRvIGxvYWQgZGF0YSBpbiBSU3R1ZGlvOiAqKkZpbGUg4oaSIE9wZW4gRmlsZSDihpIqKiBjbGljayB0aGUgYC5SRGF0YWAKPiBvciBgLmNzdmAgZmlsZS4KCmBgYHtyIGNsZWFuLW1lbX0KIyBSZW1vdmUgZGF0YXNldHMgd2Ugbm8gbG9uZ2VyIG5lZWQgZnJvbSBtZW1vcnkKcm0oYEVHMDEtMTlDT0xMRUdFYCkKcm0obXlkYXRhKQpgYGAKCiMjIEV4cGxvcmUgdGhlIERhdGEgRnJhbWUKCkluIFJTdHVkaW8geW91IHdvdWxkIHJ1biBgVmlldyhkZilgOyBoZXJlIHdlIHByZXZpZXcgdGhlIGZpcnN0IHJvd3MgaW5zdGVhZC4KCmBgYHtyIHZpZXctZGZ9CmtuaXRyOjprYWJsZShoZWFkKGRmKSwgY2FwdGlvbiA9ICJGaXJzdCByb3dzIG9mIHRoZSBDb2xsZWdlIGRhdGEiKQpgYGAKCmBgYHtyIHN0cnVjdHVyZX0Kc3RyKGRmKSAgICAgICAgIyBzdHJ1Y3R1cmU6IHR5cGVzICsgc2FtcGxlIHZhbHVlcwpoZWFkKGRmLCAxMCkgICAjIGZpcnN0IHRlbiByb3dzCmRmJFN0YXRlICAgICAgICMgYWNjZXNzIGEgY29sdW1uIGJ5IG5hbWUKYGBgCgpgYGB7ciBkaW1lbnNpb25zfQpkaW0oZGYpICAgICAgICAjIHJvd3MgeCBjb2x1bW5zCm5yb3coZGYpICAgICAgICMgbnVtYmVyIG9mIHJvd3MKbmNvbChkZikgICAgICAgIyBudW1iZXIgb2YgY29sdW1ucwpgYGAKCmBgYHtyIG5hbWVzfQpuYW1lcyhkZikgICAgICAjIHZhcmlhYmxlIG5hbWVzCmNvbG5hbWVzKGRmKSAgICMgc2FtZSBhcyBuYW1lcygpCnJvd25hbWVzKGRmKSAgICMgcm93IG5hbWVzIChyYXJlbHkgbmVlZGVkKQpgYGAKCkRlc2NyaXB0aXZlIHN0YXRpc3RpY3M6CgpgYGB7ciBkZXNjcmlwdGl2ZXN9CnN1bW1hcnkoZGYpICAgICAgICAgICAgIyBzdW1tYXJ5IG9mIGFsbCB2YXJpYWJsZXMKc3VtbWFyeShkZiRQb3B1bGF0aW9uKSAjIHN1bW1hcnkgb2Ygb25lIGNvbHVtbgoKbWVhbihkZiRQb3B1bGF0aW9uKSAgICAjIHVzZSBhIGNvbHVtbiwgTk9UIG1lYW4oZGYpCm1lZGlhbihkZiRQb3B1bGF0aW9uKQptaW4oZGYkUG9wdWxhdGlvbikKbWF4KGRmJFBvcHVsYXRpb24pCmBgYAoKKipUT0RPIChkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzKS4qKiBVc2luZyBgRUcwMS0xOUNPTExFR0UuY3N2YDoKCjEuIENhbGN1bGF0ZSB0aGUgbWVhbiBvZiB0aGUgYFVuZGVyZ3JhZHNgIGNvbHVtbiB3aXRoIGBtZWFuKClgLgoyLiBDYWxjdWxhdGUgdGhlIG1lZGlhbiBvZiB0aGUgYFVuZGVyZ3JhZHNgIGNvbHVtbiB3aXRoIGBtZWRpYW4oKWAuCgpgYGB7ciBkZXNjcmlwdGl2ZXMtdG9kb30KbWVhbihkZiRVbmRlcmdyYWRzKQptZWRpYW4oZGYkVW5kZXJncmFkcykKYGBgCgojIEhpc3RvZ3JhbXMgYW5kIEJveHBsb3RzIChCYXNlIFIpCgojIyBIaXN0b2dyYW1zIChjb250aW51b3VzIHZhcmlhYmxlcykKCkhpc3RvZ3JhbXMgc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgdmFyaWFibGUg4oCUIGFyZSB2YWx1ZXMgY29uY2VudHJhdGVkLApzcHJlYWQgb3V0LCBvciBza2V3ZWQ/CgpgYGB7ciBoaXN0LWJhc2ljfQpoaXN0KGRmJFBvcHVsYXRpb24pICAgICAgICAgICAgICAgICMgYmFzaWMKYGBgCgpgYGB7ciBoaXN0LWJyZWFrc30KaGlzdChkZiRQb3B1bGF0aW9uLCBicmVha3MgPSAyMCkgICAjIGFkanVzdCBudW1iZXIgb2YgYmlucwpoaXN0KGRmJFBvcHVsYXRpb24sIGJyZWFrcyA9IDEwMCkKYGBgCgpgYGB7ciBoaXN0LWxhYmVsc30KaGlzdChkZiRQb3B1bGF0aW9uLCBicmVha3MgPSAyMCwKICAgICBtYWluID0gIlBvcHVsYXRpb24gKEhpc3RvZ3JhbSkiLAogICAgIHhsYWIgPSAiUG9wdWxhdGlvbiIsIHlsYWIgPSAiRnJlcXVlbmN5IiwgY29sID0gImxpZ2h0Z3JlZW4iKQpgYGAKCiMjIEJveHBsb3RzIChxdWFydGlsZXMgYW5kIG91dGxpZXJzKQoKQm94cGxvdHMgc2hvdyB0aGUgbWVkaWFuLCBsb3dlciBhbmQgdXBwZXIgcXVhcnRpbGVzLCBhbmQgb3V0bGllcnMuCgpgYGB7ciBib3gtYmFzaWN9CmJveHBsb3QoZGYkUG9wdWxhdGlvbikgICAgICAgICAgICAgICAgICAgICAjIGJhc2ljIHZlcnRpY2FsCmBgYAoKYGBge3IgYm94LWhvcml6b250YWx9CmJveHBsb3QoZGYkUG9wdWxhdGlvbiwgaG9yaXpvbnRhbCA9IFRSVUUpICAjIGhvcml6b250YWwgc3R5bGUKYGBgCgpgYGB7ciBib3gtbGFiZWxzfQpib3hwbG90KGRmJFBvcHVsYXRpb24sIGhvcml6b250YWwgPSBUUlVFLAogICAgICAgIG1haW4gPSAiUG9wdWxhdGlvbiAoQm94cGxvdCkiLAogICAgICAgIHhsYWIgPSAiUG9wdWxhdGlvbiIsIGNvbCA9ICJwdXJwbGUiKQpgYGAKCiMgVmlzdWFsaXphdGlvbiB3aXRoIGBnZ3Bsb3QyYAoKQnVpbGQgcGxvdHMgbGF5ZXIgYnkgbGF5ZXI6IHN0YXJ0IHdpdGggYGRhdGFgICsgYGFlcygpYCArIGdlb21zLgpgZ2dwbG90MmAgaXMgYSAqZ3JhbW1hciBvZiBncmFwaGljcyouIFJlbWVtYmVyOiAoMSkgdXNlIGArYCB0byBhZGQgbGF5ZXJzLCBhbmQKKDIpIHRoZSBvcmRlciBtYXR0ZXJzLgoKYGBge3IgbG9hZC1nZ3Bsb3R9CmxpYnJhcnkoZ2dwbG90MikKYGBgCgojIyBTY2F0dGVycGxvdCAoR2FwbWluZGVyIGRhdGEpCgpHb2FsOiBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBHRFAgcGVyIGNhcGl0YSBhbmQgbGlmZSBleHBlY3RhbmN5LgoKYGBge3IgZ2FwbWluZGVyfQpsaWJyYXJ5KGdhcG1pbmRlcikKZ20gPC0gZ2FwbWluZGVyCgpuYW1lcyhnbSkKYGBgCgpgYGB7ciBzY2F0dGVyLWJhc2V9CmdncGxvdChnbSwgYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwKSkgICAjIGJhc2UgbGF5ZXIgb25seQpgYGAKCmBgYHtyIHNjYXR0ZXItcG9pbnRzfQpnZ3Bsb3QoZGF0YSA9IGdtLCBhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHApKSArCiAgZ2VvbV9wb2ludCgpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhZGQgc2NhdHRlciBwb2ludHMKYGBgCgpgYGB7ciBzY2F0dGVyLWxhYnN9CmdncGxvdChkYXRhID0gZ20sIGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnModGl0bGUgPSAiTGlmZSBFeHBlY3RhbmN5IHZzIEdEUCBwZXIgQ2FwaXRhIiwKICAgICAgIHggPSAiR0RQIHBlciBDYXBpdGEiLCB5ID0gIkxpZmUgRXhwZWN0YW5jeSIpCmBgYAoKYGBge3Igc2NhdHRlci1zbW9vdGh9CmdncGxvdChkYXRhID0gZ20sIGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnModGl0bGUgPSAiTGlmZSBFeHBlY3RhbmN5IHZzIEdEUCBwZXIgQ2FwaXRhIiwKICAgICAgIHggPSAiR0RQIHBlciBDYXBpdGEiLCB5ID0gIkxpZmUgRXhwZWN0YW5jeSIpICsKICBnZW9tX3Ntb290aChjb2xvciA9ICJwaW5rIikgICAgICAgICAgICAgICAgICAjIHNtb290aCB0cmVuZCBsaW5lCiMgTm90ZTogZ3JleSBzaGFkaW5nID0gY29uZmlkZW5jZSBpbnRlcnZhbApgYGAKCiMjIEhpc3RvZ3JhbSAoY29udGludW91cyB2YXJpYWJsZSkKClNob3cgdGhlIGRpc3RyaWJ1dGlvbiBvZiBgUG9wdWxhdGlvbmAgZnJvbSBgRUcwMS0xOUNPTExFR0UuY3N2YC4KCmBgYHtyIGdnLWhpc3R9CmdncGxvdChkYXRhID0gZGYsIGFlcyh4ID0gUG9wdWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbSgpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGJhc2ljIGhpc3RvZ3JhbQoKZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBQb3B1bGF0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA0MCkgICAgICAgICAgICAgICAgICAgICMgYWRqdXN0IG51bWJlciBvZiBiaW5zCgpnZ3Bsb3QoZGYsIGFlcyh4ID0gUG9wdWxhdGlvbikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDAsIGZpbGwgPSAic3RlZWxibHVlIikgKwogIGxhYnModGl0bGUgPSAiUG9wdWxhdGlvbiAoSGlzdG9ncmFtKSIsIHggPSAiUG9wdWxhdGlvbiIsIHkgPSAiQ291bnQiKQpgYGAKCiMjIEJhciBDaGFydCAoY2F0ZWdvcmljYWwgdmFyaWFibGUpCgpHb2FsOiBkcmF3IGEgYmFyIGNoYXJ0IHVzaW5nIHRoZSBgRUcwMS0wN09OTElORS5yZGFgIGRhdGFzZXQuCgpgYGB7ciBiYXJ9CmxvYWQoIkVHMDEtMDdPTkxJTkUucmRhIikKZGYyIDwtIGBFRzAxLTA3T05MSU5FYCAgICMgcmVuYW1lIHRoZSBkYXRhIGZyYW1lCgpnZ3Bsb3QoZGYyLCBhZXMoeCA9IFNvdXJjZSwgeSA9IENvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSAgICAgICAgICAgICAgICAgICMgYmFzaWMgYmFyIGNoYXJ0CgpnZ3Bsb3QoZGF0YSA9IGRmMiwgYWVzKHggPSBTb3VyY2UsIHkgPSBDb3VudCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJmb3Jlc3RncmVlbiIpICsKICBsYWJzKHRpdGxlID0gIlNvdXJjZSAoQmFyIENoYXJ0KSIsIHggPSAiU291cmNlIiwgeSA9ICJDb3VudHMiKQpgYGAKCmBgYHtyIGJhci1vcmRlcn0KIyBDaGFuZ2UgdGhlIG9yZGVyIG9mIHRoZSBiYXJzCm9yZGVyIDwtIGMoIkxpYnJhcnkiLCAiR29vZ2xlIiwgIldpa2lwZWRpYSIsICJPdGhlciIpCgpnZ3Bsb3QoZGF0YSA9IGRmMiwgYWVzKHggPSBTb3VyY2UsIHkgPSBDb3VudCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJmb3Jlc3RncmVlbiIpICsKICBsYWJzKHRpdGxlID0gIlNvdXJjZSAoQmFyIENoYXJ0KSIsIHggPSAiU291cmNlIiwgeSA9ICJDb3VudHMiKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBvcmRlcikKYGBgCgoqKlRPRE8gKHZpc3VhbGl6YXRpb24pLioqIFVzaW5nIHRoZSBnYXBtaW5kZXIgZGF0YSwgZHJhdyBhIGJhciBjaGFydCBvZgpgY29udGluZW50YCAoeCkgdnMuIGBsaWZlRXhwYCAoeSkuIFBpY2sgeW91ciBmYXZvcml0ZSBgZmlsbGAgY29sb3IsIGFkZCBhIHRpdGxlCmFuZCBheGlzIGxhYmVscyB3aXRoIGBsYWJzKClgLCBhbmQgcmVvcmRlciB0aGUgY29udGluZW50cyBhczogQW1lcmljYXMsIEFzaWEsCkFmcmljYSwgT2NlYW5pYSwgRXVyb3BlLgoKYGBge3IgYmFyLXRvZG99Cm9yZGVyMiA8LSBjKCJBbWVyaWNhcyIsICJBc2lhIiwgIkFmcmljYSIsICJPY2VhbmlhIiwgIkV1cm9wZSIpCgpnZ3Bsb3QoZGF0YSA9IGdtLCBhZXMoeCA9IGNvbnRpbmVudCwgeSA9IGxpZmVFeHApKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiYnJvd24iKSArCiAgbGFicyh0aXRsZSA9ICJDb250aW5lbnQgdnMuIExpZmUgRXhwZWN0YW5jeSIsCiAgICAgICB4ID0gIkNvbnRpbmVudCIsIHkgPSAiTGlmZSBFeHBlY3RhbmN5IikgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gb3JkZXIyKQpgYGAKCiMgVmFyaWFibGUgVHJhbnNmb3JtYXRpb24KCmBgYHtyIGdwYS1pbXBvcnR9CmdwYSA8LSByZWFkLmNzdigiRVgxNi0xOUdQQS5jc3YiKQoKa25pdHI6OmthYmxlKGhlYWQoZ3BhKSwgY2FwdGlvbiA9ICJGaXJzdCByb3dzIG9mIHRoZSBHUEEgZGF0YSIpCmBgYAoKKipDb2RlYm9vay4qKgoKLSBgR1BBYCA9IGdyYWRlLXBvaW50IGF2ZXJhZ2UKLSBgSFNNYCwgYEhTU2AsIGBIU0VgID0gaGlnaC1zY2hvb2wgbWF0aCAvIHNjaWVuY2UgLyBFbmdsaXNoIGdyYWRlcwotIGBTQVRNYCwgYFNBVENSYCwgYFNBVFdgID0gU0FUIE1hdGggLyBDcml0aWNhbCBSZWFkaW5nIC8gV3JpdGluZwotIGBzZXhgID0gMSBtYWxlLCAyIGZlbWFsZQoKQSAqKmxvZyB0cmFuc2Zvcm1hdGlvbioqIGlzIG9mdGVuIHVzZWQgdG8gcmVkdWNlIHNrZXduZXNzLiBGb3IgZXhhbXBsZSwgY3JlYXRlIGEKbG9nIHZlcnNpb24gb2YgdGhlIFNBVCBNYXRoIHNjb3JlczoKCmBgYHtyIGxvZy1zYXRtfQpncGEkbG9nU0FUTSA8LSBsb2coZ3BhJFNBVE0pCgpoaXN0KGdwYSRTQVRNLCBicmVha3MgPSAyMCkKaGlzdChncGEkbG9nU0FUTSkKYGBgCgpUaGUgbG9nIG1ha2VzIGxhcmdlIG51bWJlcnMgbW9yZSBjb21wYXJhYmxlIGFuZCBjYW4gbGluZWFyaXplIGNlcnRhaW4KcmVsYXRpb25zaGlwcy4KCioqVE9ETyAobG9nIHRyYW5zZm9ybWF0aW9uKS4qKiBNYWtlIGEgbG9nIHZlcnNpb24gb2YgdGhlIFNBVCBXcml0aW5nIHNjb3JlIGFuZApjb21wYXJlIGhpc3RvZ3JhbXMgb2YgdGhlIHJhdyBhbmQgbG9nLXRyYW5zZm9ybWVkIHZhbHVlcy4KCmBgYHtyIGxvZy1zYXR3fQpncGEkbG9nU0FUVyA8LSBsb2coZ3BhJFNBVFcpCgpoaXN0KGdwYSRTQVRXLCBicmVha3MgPSAyMCkKaGlzdChncGEkbG9nU0FUVykKYGBgCgojIENvcnJlbGF0aW9uCgpDb3JyZWxhdGlvbiBtZWFzdXJlcyBob3cgdHdvIHZhcmlhYmxlcyBtb3ZlIHRvZ2V0aGVyLgoKYGBge3IgY29yci1zY2F0dGVyfQojIFNjYXR0ZXJwbG90OiBTQVQgV3JpdGluZyB2cy4gU0FUIENyaXRpY2FsIFJlYWRpbmcKZ2dwbG90KGRhdGEgPSBncGEsIGFlcyh4ID0gU0FUVywgeSA9IFNBVENSKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyIGNvcnItY29lZn0KIyBDb3JyZWxhdGlvbiBjb2VmZmljaWVudApjb3IoZ3BhJFNBVFcsIGdwYSRTQVRDUiwgdXNlID0gImNvbXBsZXRlLm9icyIpCmBgYAoKQSBjb21tb24gdGhyZXNob2xkIGZvciBoaWdoIGNvcnJlbGF0aW9uIGlzICoqMC43IG9yIGFib3ZlKiouCgoqKlRPRE8gKGNvcnJlbGF0aW9uKS4qKiBEcmF3IGEgc2NhdHRlcnBsb3Qgb2YgYEdQQWAgdnMuIGBIU01gIChoaWdoLXNjaG9vbCBtYXRoCmdyYWRlcykgYW5kIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQuCgpgYGB7ciBjb3JyLXRvZG99CmdncGxvdChkYXRhID0gZ3BhLCBhZXMoeCA9IEdQQSwgeSA9IEhTTSkpICsKICBnZW9tX3BvaW50KCkKCmNvcihncGEkR1BBLCBncGEkSFNNLCB1c2UgPSAiY29tcGxldGUub2JzIikKYGBgCgojIExpbmVhciBSZWdyZXNzaW9uCgpSZWdyZXNzaW9uIGlzIGFib3V0IHByZWRpY3RpbmcgYSByZWxhdGlvbnNoaXAg4oCUIGRyYXdpbmcgdGhlIGJlc3QtZml0IGxpbmUKdGhyb3VnaCB0aGUgZGF0YS4gV2UgZXN0aW1hdGUgaXQgd2l0aCBgbG0oeSB+IHgsIGRhdGEgPSApYC4KCmBgYHtyIGxtLWZpdH0KIyBQcmVkaWN0IFNBVCBDcml0aWNhbCBSZWFkaW5nIGZyb20gU0FUIFdyaXRpbmcKbTEgPC0gbG0oU0FUQ1IgfiBTQVRXLCBkYXRhID0gZ3BhKQojIE5vdGU6IHVzZSBhIG5ldyBuYW1lIGZvciBlYWNoIG1vZGVsLCBvciBpdCB3aWxsIG92ZXJ3cml0ZSB0aGUgb2xkIG9uZS4KCnN1bW1hcnkobTEpCmBgYAoKYGBge3IgbG0tcGxvdH0KIyBTY2F0dGVycGxvdCArIHJlZ3Jlc3Npb24gbGluZQpnZ3Bsb3QoZ3BhLCBhZXMoeCA9IFNBVFcsIHkgPSBTQVRDUikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBjb2xvciA9ICJyZWQiKQojIEdyYXkgc2hhZGVkIGFyZWE6IGNvbmZpZGVuY2UgaW50ZXJ2YWwgKHRoZSBtYXJnaW4gb2YgdW5jZXJ0YWludHkpCmBgYAoKKipUT0RPIChyZWdyZXNzaW9uKS4qKiBQcmVkaWN0IGBTQVRDUmAgZnJvbSBgU0FUTWAsIGludGVycHJldCB0aGUgb3V0cHV0LCB0aGVuCnBsb3QgdGhlIHNjYXR0ZXJwbG90IHdpdGggdGhlIHJlZ3Jlc3Npb24gbGluZSBhZGRlZC4KCmBgYHtyIGxtLXRvZG99Cm0yIDwtIGxtKFNBVENSIH4gU0FUTSwgZGF0YSA9IGdwYSkKc3VtbWFyeShtMikKCmdncGxvdChncGEsIGFlcyh4ID0gU0FUTSwgeSA9IFNBVENSKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIGNvbG9yID0gInJlZCIpCmBgYAo=