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

  1. Quick reminders: new R session setup
  2. Working with data frames (review from Lab 1)
  3. Histograms and boxplots (Base R)
  4. Visualization with ggplot2
  5. Variable transformation
  6. Correlation
  7. Linear regression

1 Quick Reminders: New R Session Setup

Always follow these steps when you open RStudio:

  1. Install packages (only once on your computer).
  2. Load packages (every new session).
  3. Set your working directory.
  4. 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.

2 Working with Data Frames

2.1 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)

2.2 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
State Undergrads Population UGradPerThou
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"
dim(df)        # rows x columns
#> [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
median(df$Population)
#> [1] 4221537
min(df$Population)
#> [1] 512573
max(df$Population)
#> [1] 36121296

TODO (descriptive statistics). Using EG01-19COLLEGE.csv:

  1. Calculate the mean of the Undergrads column with mean().
  2. Calculate the median of the Undergrads column with median().
mean(df$Undergrads)
#> [1] 302135.9
median(df$Undergrads)
#> [1] 203641

3 Histograms and Boxplots (Base R)

3.1 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")

3.2 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")

4 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.

library(ggplot2)

4.1 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

4.2 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")

4.3 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)

5 Variable Transformation

gpa <- read.csv("EX16-19GPA.csv")

knitr::kable(head(gpa), caption = "First rows of the GPA data")
First rows of the GPA data
obs GPA HSM HSS HSE SATM SATCR SATW sex
1 3.84 10 10 10 630 570 590 2
2 3.97 10 10 10 750 700 630 1
3 3.49 8 10 9 570 510 490 2
4 1.95 6 4 8 640 600 610 1
5 2.59 8 10 9 510 490 490 2
6 3.00 7 10 10 660 680 630 1

Codebook.

  • GPA = grade-point average
  • HSM, HSS, HSE = high-school math / science / English grades
  • SATM, SATCR, SATW = SAT Math / Critical Reading / Writing
  • sex = 1 male, 2 female

A log transformation is often used to reduce skewness. For example, create a log version of the SAT Math scores:

gpa$logSATM <- log(gpa$SATM)

hist(gpa$SATM, breaks = 20)

hist(gpa$logSATM)

The log makes large numbers more comparable and can linearize certain relationships.

TODO (log transformation). Make a log version of the SAT Writing score and compare histograms of the raw and log-transformed values.

gpa$logSATW <- log(gpa$SATW)

hist(gpa$SATW, breaks = 20)

hist(gpa$logSATW)

6 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

7 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=