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

  1. Getting started with RStudio
  2. Installing & loading packages
  3. Working directory setup
  4. Basic R commands
  5. Working with data frames
  6. Histograms and boxplots
  7. Visualization with ggplot2

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

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

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

4 Basic R Commands

You can use R as a calculator. Be careful: R is case sensitivea and A are treated as different variables (so blue, Blue, and BLUE are three different names).

a <- 3
A <- 4
a
#> [1] 3
A
#> [1] 4
2 + 2
#> [1] 4
10 - 4
#> [1] 6
3 * 4
#> [1] 12
12 / 3
#> [1] 4
a + A
#> [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
y[2]
#> [1] "puppy"

Functions.

mean(x)
#> [1] 3
sqrt(A)
#> [1] 2

Use ?command (or help(command)) to get help in RStudio:

?mean
help(sqrt)

5 Working with Data Frames

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

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

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.

dim(df)        # rows x columns
#> [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
mean(df$Population)
#> [1] 5955551
median(df$Population)
#> [1] 4221537
min(df$Population)
#> [1] 512573
max(df$Population)
#> [1] 36121296

Try it. What is the mean of the undergrad population in the dataset? What about the median? (Type your own code.)

6 Histograms and Boxplots

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

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

7.1 Scatterplot (Gapminder dataset)

Goal: explore the relationship between GDP per capita and life expectancy.

gm <- gapminder
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

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

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