Workshop goals

By the end of this workshop you should be able to:

Getting started

Installing R and RStudio

Working with R is primarily text-based. The basic mode of use for R is that the user types in a command in the R language and presses enter, and then R computes and displays the result.

RStudio

We will be working in RStudio. This surrounds the console, where one enters commands and views the results, with various conveniences. In addition to the console, RStudio provides panels containing:

  • A text editor, where R commands can be recorded for future reference.
  • A history of commands that have been typed on the console.
  • An “environment” panel with a list of variables, which contain values that R has been told to save from previous commands.
  • A file manager.
  • Help on the functions available in R (Using a question mark before the function)
  • A panel to show plots (graphs).

R script vs RMD (Markdowns and Notebooks)

  • To start an R script click ctrl + shift + N or cmd + shift + N on mac You can run lines of code, but the output won’t be saved

  • To start an RMD click ctrl + B/N or cmd + B/N on mac to initialize a notebook
    Allows you to run and visualize output in the file, easy to share with collaborators showing both the code and all results/plots

This is called the main chunk, where is has all the settings that you would like to apply along the notebook

knitr::opts_chunk$set(echo = T, results = "show")
options(width=80)
require("knitr")
Loading required package: knitr
## To change the directory in a notebook/markdown
# opts_knit$set(root.dir = "path/to/directory/")

To insert a chunk, use ctrl + alt + I, or command + option + I

R Basics

The console

Open RStudio, click on the “Console” panel, type 3+2 and press enter. R displays the result of the calculation. In this document, we will be showing such an interaction with R as below.

3 + 2
[1] 5

+ is called an operator. R has other operators for basic mathematical calculations:
- for subtraction
* for multiplication
/ forward slash for division
^ hat for “power”

* has higher precedence than +. We can use brackets ( ), if necessary. Try 1+2*3 and (1+2)*3

1+2*3
[1] 7
(1+2)*3
[1] 9

Making lines of code according to styler, highlight the lines of code, click ctrl + shift + A or cmd + shift + A

Comparisons

We use >, <, >=, ==, <=, != for comparisons. Note that for comparisons its always a double sign to be used. This returns a “logical” value of TRUE or FALSE.

3 * 3 == 9
[1] TRUE

This literally means Is the left hand side equal to the right hand side? Is 3 times 3 equal to 9?

Variables

A variable is a name that stores a value. We create a variable by assigining a value to it using <- which points to the left assigning the value of the right to the one on the left

weight_kg = 65
weight_kg <- 65

To print the value stored in a variable, print the variable

weight_kg
[1] 65

Names of the variables should not contain special characters or spaces, should not start with a number. Dots are ok, unlike many other programming languages

To list all variables in an environment

ls()
[1] "weight_kg"
## to remove a variable/object
rm(weight_kg)

## to remove some variable of a certain pattern (starting/ending/including ....)
rm(list = ls(pattern = ""))

Any line of code inside a chunk or in the console that has a # before, is a comment. Useful to document your code so its easy to remember why we used this command

To add a # at the beginning of the line for multiple lines, highlight all lines, and click ctrl + shift + c or cmd + shift + c

We can do arithemtic on the variable

weight_kg <- 65
2.2 * weight_kg
[1] 143

Vectors

A vector is a collection of elements (numbers, words,.. etc) To be able to combine elements into a vector, we use the function c() which stands for combine or concatenate

c(1,2,3,4)
[1] 1 2 3 4
vec <- c(10,20,30,40)
vec + 1 # Here the arithmetic + 1 is applied on every element of the vector
[1] 11 21 31 41

We can also combine vectors

vec + vec 
[1] 20 40 60 80
# In this case, the 1st element of vec is added to the 1st element of vec, 2nd with 2nd.. and so on

To check the length of the vector we use the function length()

length(vec)
[1] 4

Data types in R

Most common data types in R are mainly:
* Numeric (could be double which is the default, or integer)
* Character (String)
* Logical (TRUE/FALSE)

To check the type of a vector, we use typeof() function

x <- 5
typeof(x)
[1] "double"
## To create an integer add L to the number
x <- 5L
typeof(x)
[1] "integer"
print(x)
[1] 5
class(x)
[1] "integer"

For Characters, we use single or double quotes

"Hello World"
[1] "Hello World"
plain_txt = 'This is a string'
print(plain_txt)
[1] "This is a string"
typeof(plain_txt)
[1] "character"
class(plain_txt)
[1] "character"

To be able to look for and modify a pattern, the vector should be a string!

There are also categorical vectors in which elements can be one of several “categories/levels”

factor(c("mutant", "wildtype", "mutant"), 
       levels = c("wildtype", "mutant"))
[1] mutant   wildtype mutant  
Levels: wildtype mutant

Factor is important for arrangement of data in a plot for example.

Accessing elements of vectors (Indexing)

To access elements, we use [] with a number (index)

vec[1]
[1] 10
vec[-1]
[1] 20 30 40
vec[c(1,3)]
[1] 10 30
vec[1:4]
[1] 10 20 30 40
vec[1] <- 6

We can use a vector instead of numbers to index a vector

myindex <- c(4,2,1,3)
vec[myindex]
[1] 40 20  6 30
## which is the same as
vec[c(4,2,1,3)]
[1] 40 20  6 30

Matrix

2-D tabular data structure in which all elements are of the same type (typically numeric matrices, but could also be character or logical)

# To create a matrix
matrix()
     [,1]
[1,]   NA
# Example:
mat <- matrix(1:12, nrow = 2, ncol = 6, byrow = F)
mat
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    3    5    7    9   11
[2,]    2    4    6    8   10   12
typeof(mat)
[1] "integer"
class(mat)
[1] "matrix" "array" 
# To get the dimensions of a matrix
dim(mat)
[1] 2 6
## get only how many rows
nrow(mat)
[1] 2
## get only how many columns
ncol(mat)
[1] 6
## get column names
colnames(mat)
NULL
## Accessing elements of a matrix
mat[1,3] # row number, # col number]
[1] 5
# this retuns the element at row 1, column 3

Data frame

Similar to a matrix, the difference is that the columns of a dataframe could be of different types (could combine numeric values, strings, logical) The index of each row could be from 1 to length(dataframe) or could have a unique name (can be accessed/modified using rownames function)

Functions

a function is followed by parentheses () mean(), sd(), print(), rep()

# To create a function
name_of_function <- function(){ # inside parentheses what the function takes
  # what the function should do
}

## then call the function
name_of_function()

The function takes arguments (options), some are mandatory and some are optional. Arguments can be supplied in order (positional argument), or by the argument name (better to use). Clicking tab for autocompletion helps us remember what arguments a function could take

calc <- function(num1, num2){
  sum = num1 + num2
  print(sum)
}

calc(num1 = 10, 5)
[1] 15

Lists

Lists can contain different kinds of elements

mylist <- list(num= 42, greeting="Hello, World")

# to access elements: ## double square bracket to access
mylist[[1]]
[1] 42
mylist$greeting
[1] "Hello, World"

If you are not sure about the type of a variable, run class

class(vec)
[1] "numeric"
class(mat)
[1] "matrix" "array" 
str(mylist)
List of 2
 $ num     : num 42
 $ greeting: chr "Hello, World"

Applying a function on a list (very practical)

list_nums <- list(
  a = c(1, 2, 3),
  b = c(10, 20, 30),
  c = c(5, 5, 5)
)

lapply(list_nums, mean)
$a
[1] 2

$b
[1] 20

$c
[1] 5

Applying an anonymous function (when we want to perform many steps over the list)

list_nums_modified <- lapply(list_nums, function(element){
  ## first do this on each element
  element + 4
  ## then do this
  return(element)
})
## Applying the mean function over a list
lapply(list_nums, function(elem) elem^2) ## returns a list
$a
[1] 1 4 9

$b
[1] 100 400 900

$c
[1] 25 25 25

sapply (simple apply)

for a simplified version

sapply(list_nums, function(elem) elem^2) ## returns a simplified form (vector)
     a   b  c
[1,] 1 100 25
[2,] 4 400 25
[3,] 9 900 25

Loops

for (numb in list_nums){
  print(numb)
}
[1] 1 2 3
[1] 10 20 30
[1] 5 5 5

If conditional

list_nums <- c(1,2,3,6,7)
for(numb in list_nums){
  if(numb > 5){
    print(numb)}
    else print("This number is smaller than 5, so will not be printed")
}
[1] "This number is smaller than 5, so will not be printed"
[1] "This number is smaller than 5, so will not be printed"
[1] "This number is smaller than 5, so will not be printed"
[1] 6
[1] 7

Tidy data

Tidy data rules: 1. Each variable = one column 2. Each observation = one row 3. Each type of entity = one table

Why it matters: - easier plotting - easier modelling - fewer bugs - compatible with tidyverse grammar

Installing packages

To install a package from CRAN, we use the function install.packages

install.packages("styler")
install.packages("tidyverse")

From Github

Github is a web-based platform for version control, collaboration, and code sharing, built around the Git system. It allows users to track changes to files, manage contributions from multiple people, and publish open-source projects.

GitHub is widely used for:

  • Sharing R packages before they are published on CRAN

  • Developing packages collaboratively using pull requests and issues

  • Storing analysis projects (scripts, R Markdown, data, documentation)

  • Reproducible workflows, since every change is tracked

Installing a package from Github install_github("author/package")

install.packages("devtools")

## To load a library
library(devtools)
devtools::install_github("githubusername/repository")
install_github("immunogenomics/presto")

## instead of loading a whole package, we can call a function from a package using the format:
package::function()
  
## function select
AnnotationDbi::select()
dplyr::select()
  
## Example
devtools::install_github("immunogenomics/presto")

From Bioconductor

Bioconductor

Bioconductor is an open-source project that provides a large collection of R packages for biological and biomedical data analysis, especially high-throughput sequencing, genomics, transcriptomics, and proteomics.

  • Curated, peer-reviewed packages: Every package goes through a structured review for code quality, documentation, and reproducibility.

  • Focus on life sciences: Tools for RNA-seq, ChIP-seq, single-cell RNA-seq, epigenomics, variant analysis, annotation, and more.

  • Reproducible workflows using standardized data structures (e.g., SummarizedExperiment, SingleCellExperiment).

  • Frequent release cycle (twice yearly), synchronized with R versions.

Bioconductor has become a central ecosystem for computational biology because it promotes reproducibility, integrates with modern R workflows, and provides consistent interfaces for complex biological datasets.

## First, to install Bioconductor
if (!require("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install(version = "3.22")

## Installing packages from Bioconductor
BiocManager::install("DESeq2")
install.packages("tidyverse")
install.packages("janitor")
install.packages("lubridate")

Loading libraries

library(tidyverse)
library(tidyr)
library(dplyr)
library(stringr)
library(readr)
library(janitor)
library(lubridate)

# To load multiple libraries at once, we can use pacman package, p_load function
pacman::p_load(tidyverse, styler, dplyr, stringr, janitor, lubridate)

Working with directories

## To check the current directory
getwd()
## To change the working directory
setwd("Path/to/directory/")

## Remember to change the directory for the whole notebook, we use the main chunk

Importing data

## Importing an R data structure object (.RDS)
myfile <- readRDS(file = "path/to/file.RDS")

## CSV file
myfile <- utils::read.csv()

## More general function
myfile <- utils::read.delim(file = "/path/to/file.extenstion", 
                  header = T, sep = "\t") ## the file has a header with column names, and each observation is separated by a tab
myfile <- readxl::read_xlsx("/path/to/file/file.xlsx")

Checking the content of a file

head(myfile) ## shows the first 6 observations, can change n argument
tail(myfile) ## shows the last 6 observations, can change n argument
str(myfile) ## checks the structure of the file as a variable
dim(myfile) ## checks the dimensions of a file (returns number of rows, number of columns)
nrow(myfile) # checks the number of rows
ncol(myfile)# checks the number of columns

## checks the column names
colnames(myfile)
names(myfile)

Data maniuplation

Adding columns to a dataframe

myfile$newcol <- myfile$col1 * myfile$col2

Access columns by name

# one column
myfile$columnname
## more than a column
myfile[, c("col1", "col4", "col2")]
myfile[, c(1,4,2)]

Access a column by index

myfile[,4] # the row is empty, which means show all rows for column 4

Access rows

df[4, ] ## gets the 4th row for all columns
df[4:10] ## from 4th to 10th

Select rows by a condition (exact)

myfile_subset <- subset(myfile, given_column == "something") ## subset rows that follow this condition

Select rows by a condition (more than one category)

myfile_subset <- subset(myfile, 
                        given_column %in% c("something", "something else")) ## subset rows that follow this condition

Select rows by a condition (exclusion of exact)

myfile_subset <- subset(myfile, given_column != "something") ## subset rows that follow this condition

Select rows by a condition (exclusion of more than one category)

myfile_subset <- subset(myfile, given_column != "something" & 
         given_column != "something else" &
         given_column != "another thing")

Using the ! to negate

myfile_subset <- subset(myfile,
                        !(given_column %in% c("something", "something else")))

There is no equivalent operator for %in%, but you can create it!

"%out%" <- Negate("%in%")
myfile_subset <- subset(myfile, 
                        given_column %out% c("something", "something else")) 
## subset rows that do not follow this condition
# clean column names
# df <- janitor::clean_names(df)

# install.packages("tidytuesdayR")
# tuesdata <- tidytuesdayR::tt_load('2023-10-24')
# df <- tuesdata$patient_risk_profiles

df <- readxl::read_xlsx("tuesdata_df.xlsx")
head(df)
df$`predicted risk of Sudden Vision Loss, with no eye pathology causes`
  [1] 1.115083e-04 1.607285e-03 1.458917e-04 1.527737e-04 3.258655e-04 4.667266e-04
  [7] 2.140686e-04 1.600603e-04 2.184795e-04 6.122239e-04 1.036383e-04 6.725240e-04
 [13] 3.901209e-05 6.773092e-05 2.323113e-04 9.834800e-05 4.658432e-05 1.349731e-04
 [19] 3.089568e-04 3.136560e-04 6.938989e-05 1.141138e-04 1.059116e-04 3.656749e-04
 [25] 4.289676e-04 1.280323e-04 6.686286e-05 1.063679e-04 2.121934e-04 7.246477e-04
 [31] 9.408668e-05 4.846494e-04 2.092650e-04 3.848392e-04 2.493011e-04 6.565204e-04
 [37] 2.682293e-04 5.494188e-04 1.903225e-04 7.163179e-05 9.823006e-05 2.013026e-04
 [43] 2.274620e-04 1.126852e-04 9.863359e-05 6.779899e-04 3.971666e-05 4.995190e-05
 [49] 5.199037e-05 8.074773e-05 1.597246e-04 5.455884e-04 1.431606e-04 4.426006e-04
 [55] 1.203483e-04 9.213029e-04 1.043975e-04 1.553771e-04 2.758162e-04 2.382385e-04
 [61] 9.461499e-05 1.672590e-04 2.414275e-04 1.585947e-04 3.395669e-04 2.180608e-05
 [67] 1.913146e-04 2.044669e-04 2.813584e-04 9.473806e-05 7.132445e-05 1.358532e-04
 [73] 1.018202e-04 2.385006e-05 4.251972e-05 6.189295e-04 5.478834e-04 9.701396e-04
 [79] 3.324462e-04 2.345282e-04 4.010593e-04 1.223014e-04 1.369716e-04 3.559392e-05
 [85] 2.381193e-05 2.206086e-04 6.609310e-04 6.652940e-05 1.531101e-04 6.950099e-05
 [91] 6.898172e-05 7.661815e-05 4.003384e-04 7.201001e-04 8.395539e-04 1.982071e-04
 [97] 4.538425e-05 5.966995e-04 4.032301e-04 3.110642e-04
df <- janitor::clean_names(df)
head(df)

Using tidyverse

Used for data wrangling and maniuplation

When we load tidyverse, by default, dplyr, readr, stringr, tibble, tidyr, purrr, and ggplot2 are loaded

library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.2     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     ── Conflicts ──────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors

We will work with care state dataset from tidy tuesday

care_state <- readr::read_csv(
  "https://raw.githubusercontent.com/rfordatascience/tidytuesday/main/data/2025/2025-04-08/care_state.csv"
)
Rows: 1232 Columns: 8── Column specification ──────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (5): state, condition, measure_id, measure_name, footnote
dbl  (1): score
date (2): start_date, end_date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
care_state %>% glimpse()
Rows: 1,232
Columns: 8
$ state        <chr> "AK", "AK", "AK", "AK", "AK", "AK", "AK", "AK", "AK", "AK", "AK", "…
$ condition    <chr> "Healthcare Personnel Vaccination", "Healthcare Personnel Vaccinati…
$ measure_id   <chr> "HCP_COVID_19", "IMM_3", "OP_18b", "OP_18b_HIGH_MIN", "OP_18b_LOW_M…
$ measure_name <chr> "Percentage of healthcare personnel who are up to date with COVID-1…
$ score        <dbl> 7.3, 80.0, 140.0, 157.0, 136.0, 136.0, NA, 196.0, 230.0, 182.0, 200…
$ footnote     <chr> NA, NA, "25, 26", "25, 26", "25, 26", "25, 26", "25, 26", "25", "25…
$ start_date   <date> 2024-01-01, 2023-10-01, 2023-04-01, 2023-04-01, 2023-04-01, 2023-0…
$ end_date     <date> 2024-03-31, 2024-03-31, 2024-03-31, 2024-03-31, 2024-03-31, 2024-0…

Q1: How many columns are there in the dataset? What columns exist? Q2: How many observations(rows)? Q3: How many different states exist in the dataset ? Q4: What categories are there in the condition column? Q5: What is being measured in the “measure_name” column? Q6: How many observations with footnote 5, 25, and 26? Q7: Which years are there in the start and end date columns?

starwars <- dplyr::starwars
head(starwars)
typeof(starwars)
[1] "list"
class(starwars)
[1] "tbl_df"     "tbl"        "data.frame"
## Get the name of all columns
colnames(starwars)
 [1] "name"       "height"     "mass"       "hair_color" "skin_color" "eye_color" 
 [7] "birth_year" "sex"        "gender"     "homeworld"  "species"    "films"     
[13] "vehicles"   "starships" 
# Or 
names(starwars)
 [1] "name"       "height"     "mass"       "hair_color" "skin_color" "eye_color" 
 [7] "birth_year" "sex"        "gender"     "homeworld"  "species"    "films"     
[13] "vehicles"   "starships" 

Functions on rows

1. Filtering

filter(dataframe, condition) like filter(dataframe, column == "something") filter(dataframe, condition1, condition2) like filter(dataframe, column == "something", column == "something")

Why it matters in healthcare/bioinformatics: you almost always filter: - QC failures - non-target tissue / condition - outliers - incomplete cases

human_starwars <- filter(starwars, 
                         species == "Human")
head(human_starwars)
filter(starwars,
       species == "Human", height > 180)

Combining a lot of functions

df <- nycflights23::flights
mean(pull(filter(filter(df, origin == "JFK"), carrier == "AA", !is.na(arr_delay)), arr_delay))
[1] 0.5470231

Using a pipe %>%. Shortcut is ctrl + shift + M or command + shift + M

df %>%
  filter(origin == "JFK") %>%
  filter(carrier == "AA") %>%
  filter(!is.na(arr_delay)) %>%
  pull(arr_delay) %>%
  mean()
[1] 0.5470231

Even nicer

df %>%
  filter(origin == "JFK",
         carrier == "AA",
         !is.na(arr_delay)) %>%
  summarise(avg_delay = mean(arr_delay)) %>% 
  pull(avg_delay)
[1] 0.5470231

NOTE
The function pull is a function that turns a column into a vector For example if you have a column called “genes”, using pull turns the column into a vector with all these genes

Common patterns

starwars %>% filter(is.na(mass))                # missing values
starwars %>% filter(!is.na(height))             # non-missing
starwars %>% filter(between(height, 150, 200))  # within a range

2. arrange() — sort rows

starwars %>% 
  arrange(desc(height))

3. distinct() — unique rows

starwars %>% 
  distinct(species, homeworld)

4. slice_max, slice_min, slice_head, and slice_tail

DEGs <- readxl::read_xlsx("DEGs.xlsx")

DEGs %>% 
  mutate(pct_diff = pct.1 - pct.2) %>% 
  relocate(pct_diff, .after = pct.2) %>% 
  filter(p_val_adj < 0.05) %>% 
  group_by(cluster) %>% 
  slice_max(n = 10, order_by = avg_log2FC)

slice_max for each group, get the top n observations slice_min for each group, get the bottom n observations slice_head for each group, get the first n observations slice_tail for each group, get the last n observations

If not grouped

slice_max() → get the top n rows overall based on a variable. slice_min() → get the bottom n rows overall based on a variable. slice_head() → get the first n rows in the dataset. slice_tail() → get the last n rows in the dataset.

Functions on columns

1. select(), rename(), relocate()

starwars %>%
  select(name, height, species, mass) %>%
  rename("weight_kg" = "mass") %>%
  relocate(weight_kg, .after = height)

rename(dataframe, newcolname = oldname) renames an existing column name to another Some R versions requires “” for column renaming

relocate function allows the new column is added in a specific position, default (last column)

2. Removing a column

The function select can be used to positively or negatively select columns (in any order)

df %>% 
  select(day, month, dep_time)

We can use select(df, -col1) for one column or multiple columns like select(df, -col1, -col2, -col3) or even better use (any_of)

cols_to_drop <- c("year", "carrier")

df %>% 
  select(-any_of(cols_to_drop))
df %>% 
  select(starts_with("dep"))

df %>% 
  select(ends_with("time"))

df %>% 
  select(contains("del"))

df %>% 
  select(matches("(delay|time)$"))
NA
df %>% 
  select(where(is.numeric))

df %>% 
  select(where(is.character))

3. Turning a column into multiple

df_time_hour <- df %>% select(time_hour)
head(df_time_hour)

The function separate(dataframe, col = "column_to_split", into = c("piece1", "piece2"), sep = "separator"))

df_time_hour <-  df_time_hour %>% separate(sep = " ", 
                col = time_hour, 
                into = c("yearmonthday", "time"), 
                remove = T)
head(df_time_hour)
df_time_hour <- df_time_hour %>% 
  separate(sep = "-", 
                col = yearmonthday, 
                into = c(NA, "month", "day"), 
                remove = T)
head(df_time_hour)

The same function can be applied on the time column, into hour, minute, seconds

df_time_hour <- df_time_hour %>% 
  separate(sep = ":", 
                col = time, 
                into = c("hr", "mn", "sec"), 
                remove = T)
head(df_time_hour)

4. uniting columns with unite()

df_time_hour %>%
  unite("newcol", month, day, hr, mn, sec, sep = "-", remove = FALSE)

5. mutate() — create new variables

Adding a column follows this structure mutate(df, "newcolumn" = df$existingcolumn) or mutate(df, newcolumn = df$existingcolumn)

starwars %>%
  mutate(height_m = height / 100, 
         bmi = mass / (height_m^2)) %>%
  select(name, height, mass, height_m, bmi)
new_df <- nycflights23::flights %>% 
  mutate("dag" = day)

new_df <- new_df %>% 
  mutate("Day_Month_Year" = str_c(new_df$day, 
                                  new_df$month, 
                                  new_df$year, sep = "_")) %>%
  relocate(Day_Month_Year, .before = year)
head(new_df)

case_when() — categorical variables

mutate with case_when can be used to add a column based on a condition of another column that follows a pattern.

mutate(NewCol = case_when(grepl(“pattern”, ExistingColumn) ~ “AddedText”, TRUE ~ “AddedText2”))

which means Assign “AddedText” if the pattern exists in the ExistingColumn, if not, assign “AddedText2”

starwars %>%
  mutate(height_group = case_when(
    height < 150 ~ "short",
    height < 190 ~ "medium",
    TRUE ~ "tall")
    ) %>%
  count(height_group)

6.1 Grouping

group_by is used to group the dataframe by a certain column, where another function could be applied on each group. For instance, here we calculate the average arrival delay for each carrier

df %>%
  group_by(carrier) %>%
  summarise(
    avg_arr_delay = mean(arr_delay, na.rm = TRUE),
    n_flights = n()
  )

6.2 group_by() + summarise()

starwars %>%
  group_by(species) %>%
  summarise(
    n = n(),
    mean_height = mean(height, na.rm = TRUE)
  ) %>%
  arrange(desc(n))

7. across() — multi-column operations

starwars %>%
  summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE)))

Merging/Joining dataframes

set.seed(12345)
patient_table <- tibble(
  patient_id = paste0("P", str_pad(1:12, 3, pad = "0")),
  diagnosis = sample(c("control", "sepsis", "cancer"), 12, replace = TRUE),
  age = sample(30:90, 12, replace = TRUE),
  sex = sample(c("F", "M"), 12, replace = TRUE)
)

site_table <- tibble(
  patient_id = paste0("P", str_pad(sample(1:12, 10), 3, pad = "0")),
  hospital = sample(c("SiteA", "SiteB", "SiteC"), 10, replace = TRUE),
  country = sample(c("US", "DE", "FR"), 10, replace = TRUE)
)

patient_table
site_table
NA

left_join()

patients_full <- patient_table %>%
  left_join(site_table, by = "patient_id")

patients_full

Debug: who did NOT match?

patient_table %>%
  anti_join(site_table, by = "patient_id")
airlines_df <- nycflights23::airlines
head(airlines_df)
flights_df <- nycflights23::flights
merged_df <- left_join(flights_df, airlines_df, by = "carrier") 

merged_df %>%  
  dplyr::rename(airlines_nm = name) %>% 
  relocate(airlines_nm, .after = carrier)

left_join(x, y) All from x
right_join(x, y) All from y
inner_join(x, y) Only matching rows
full_join(x, y) All rows from both tables, filling the missing with NAs

Reshaping data (Wide vs Long data shape)

set.seed(12345)

metadata <- tibble(
  sample_id = paste0("S", str_pad(1:24, 3, pad = "0")),
  patient_id = paste0("P", str_pad(sample(1:12, 24, replace = TRUE), 3, pad = "0")),
  tissue = sample(c("tumor", "normal"), 24, replace = TRUE),
  treatment = sample(c("drugA", "drugB", "placebo"), 24, replace = TRUE),
  sex = sample(c("F", "M"), 24, replace = TRUE),
  age = sample(30:85, 24, replace = TRUE),
  batch = sample(c("batch1", "batch2"), 24, replace = TRUE)
)

metadata

genes <- paste0("Gene", 
                str_pad(1:50, 4, 
                        pad = "0"))

counts <- matrix(
  rnbinom(50 * 24, mu = 80, size = 1),
  nrow = 50,
  ncol = 24,
  dimnames = list(genes, metadata$sample_id)
)

counts_df <- as_tibble(counts, rownames = "gene_id")
counts_df

Problem: This is “wide”. Many analyses need “long”.

1. pivot_longer(): wide -> long

counts_long <- counts_df %>%
  pivot_longer(
    cols = starts_with("S"),
    names_to = "sample_id",
    values_to = "count"
  )

counts_long

Now each row is: gene × sample.

Now we join counts with metadata

head(metadata)
counts_annot <- counts_long %>%
  left_join(metadata, by = "sample_id")

counts_annot %>% glimpse()

Here we summarise expression by group

Example: mean counts by tissue per gene

gene_summary <- counts_annot %>%
  group_by(gene_id, tissue) %>%
  summarise(mean_count = mean(count), .groups = "drop")

gene_summary

2. pivot_wider(): long -> wide

Create a gene × tissue table (2 columns: tumor/normal)

gene_wide <- gene_summary %>%
  pivot_wider(
    names_from = tissue,
    values_from = mean_count
  )

gene_wide

3. Binding rows and columns

visit_a <- tibble(
  id = c("P01","P02","P03"),
  visit = "baseline",
  crp = c(2.1, 5.4, 1.9)
)

visit_b <- tibble(
  id = c("P04","P05"),
  visit = "week4",
  crp = c(3.3, 2.8),
  wbc = c(6.1, 5.7)   # extra column not in visit_a
)

3.1 rbind vs bind_rows (stacking rows)

rbind(visit_a, visit_b)
bind_rows(visit_a, visit_b)

here bind_rows does not give an error, making sure rows are stacked, and missing columns are created and filled with NA (wbc is NA for baseline rows)

3.2 bind_cols() vs cbind() (gluing columns)

patients <- tibble(
  id = c("P01","P02","P03"),
  sex = c("F","M","F")
)

labs <- tibble(
  crp = c(2.1, 5.4, 1.9),
  wbc = c(5.8, 6.2, 5.1)
)
cbind(patients, labs)
bind_cols(patients, labs)
labs_short <- tibble(crp = c(2.1, 5.4))

cbind(patients, labs_short)
bind_cols(patients, labs_short)

Recap

1. Column-wise functions

(modify or choose columns):

  • select()

  • rename()

  • relocate()

Create or modify columns

  • mutate()

Identify columns using tidyselect helpers:

  • starts_with(), ends_with(), contains(), matches(), where()

2. Row-wise functions (filter, slice, row operations)

Keep or remove rows

  • filter()

  • slice(), slice_head(), slice_tail(), slice_min(), slice_max()

Order rows

  • arrange()

3. Group-wise functions

(operate within groups)

Grouping changes how other functions behave:

group_by(), ungroup()

4. Data-frame–wise functions

(operate on whole table)

  • bind_rows()

  • bind_cols()

  • left_join(), inner_join(), full_join(), anti_join()

Reshape data:

pivot_longer() and pivot_wider()

5. Summary functions

(usually inside summarise())

mean(), median(), sum(), n(), n_distinct(), sd(), var()

Good-to-know symbols names

() parentheses
[] square brackets
{} curly braces
* asterisk
& ampersand
^ hat
/ forward slash
\ backslash
’’ single quotes
“” double quotes
- hyphen (dash)
_ underscore
~ tilda
` backtick
! exclamation mark
. period
, comma
: colon
; semi-colon
$ dollar sign
# sharp
% percentage

LS0tCnRpdGxlOiAiUiArIHRpZHlyIChEYXRhIFdyYW5nbGluZykiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgICclZCAlQiAlWScpYCIKYXV0aG9yOiAiTW9oYW1lZCBIYXNzYW4iCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGNlcnVsZWFuCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMwplZGl0b3Jfb3B0aW9uczoKICBtYXJrZG93bjoKICAgIHdyYXA6IDcyCi0tLQoKIyBXb3Jrc2hvcCBnb2FscwoKQnkgdGhlIGVuZCBvZiB0aGlzIHdvcmtzaG9wIHlvdSBzaG91bGQgYmUgYWJsZSB0bzoKCi0gdW5kZXJzdGFuZCAqKnRpZHkgZGF0YSoqIGFuZCB3aHkgaXQgbWF0dGVycyBmb3IgZGF0YSBhbmFseXNpcwotIGNsZWFuIGFuZCB3cmFuZ2xlIGRhdGEgdXNpbmcgKipkcGx5cioqIChmaWx0ZXIsIHNlbGVjdCwgbXV0YXRlLCBzdW1tYXJpc2UpCi0gcmVzaGFwZSBkYXRhIHVzaW5nICoqdGlkeXIqKiAoYHBpdm90X2xvbmdlcigpYCwgYHBpdm90X3dpZGVyKClgLCBgc2VwYXJhdGUoKWApCi0gY29tYmluZSBkYXRhc2V0cyB1c2luZyAqKmpvaW5zKiogKGByaWdodF9qb2luKClgLCBgbGVmdF9qb2luKClgLCBgaW5uZXJfam9pbigpYCwgYGFudGlfam9pbigpYCkKLSBoYW5kbGUgcmVhbCBtZXNzeSBjYXNlczogbWlzc2luZyB2YWx1ZXMsIHdyb25nIGNvbHVtbiB0eXBlcywgbWVzc3kgSURzLCBkdXBsaWNhdGUga2V5cwoKCiMgR2V0dGluZyBzdGFydGVkCgojIyBJbnN0YWxsaW5nIFIgYW5kIFJTdHVkaW8KCi0gW1I6XShodHRwczovL2NyYW4ucnN0dWRpby5jb20vKSBodHRwczovL2NyYW4ucnN0dWRpby5jb20vCi0gW1JTdHVkaW86XShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkLykgaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9kb3dubG9hZC8KCgpXb3JraW5nIHdpdGggUiBpcyBwcmltYXJpbHkgdGV4dC1iYXNlZC4gVGhlIGJhc2ljIG1vZGUgb2YgdXNlIGZvciBSIGlzIHRoYXQgdGhlIHVzZXIgdHlwZXMgaW4gYSBjb21tYW5kIGluIHRoZSBSIGxhbmd1YWdlIGFuZCBwcmVzc2VzIGVudGVyLCBhbmQgdGhlbiBSIGNvbXB1dGVzIGFuZCBkaXNwbGF5cyB0aGUgcmVzdWx0LgoKIVtdKHJfZ3VpLmpwZykKCiMjIyBSU3R1ZGlvCgpXZSB3aWxsIGJlIHdvcmtpbmcgaW4gW1JTdHVkaW9dKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vZG93bmxvYWQvKS4gVGhpcyBzdXJyb3VuZHMgdGhlICpjb25zb2xlKiwgd2hlcmUgb25lIGVudGVycyBjb21tYW5kcyBhbmQgdmlld3MgdGhlIHJlc3VsdHMsIHdpdGggdmFyaW91cyBjb252ZW5pZW5jZXMuIEluIGFkZGl0aW9uIHRvIHRoZSBjb25zb2xlLCBSU3R1ZGlvIHByb3ZpZGVzIHBhbmVscyBjb250YWluaW5nOgoKKiBBICp0ZXh0IGVkaXRvciosIHdoZXJlIFIgY29tbWFuZHMgY2FuIGJlIHJlY29yZGVkIGZvciBmdXR1cmUgcmVmZXJlbmNlLgoqIEEgaGlzdG9yeSBvZiBjb21tYW5kcyB0aGF0IGhhdmUgYmVlbiB0eXBlZCBvbiB0aGUgY29uc29sZS4KKiBBbiAiZW52aXJvbm1lbnQiIHBhbmVsIHdpdGggYSBsaXN0IG9mICp2YXJpYWJsZXMqLCB3aGljaCBjb250YWluIHZhbHVlcyB0aGF0IFIgaGFzIGJlZW4gdG9sZCB0byBzYXZlIGZyb20gcHJldmlvdXMgY29tbWFuZHMuCiogQSBmaWxlIG1hbmFnZXIuCiogSGVscCBvbiB0aGUgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBSIChVc2luZyBhIHF1ZXN0aW9uIG1hcmsgYmVmb3JlIHRoZSBmdW5jdGlvbikKKiBBIHBhbmVsIHRvIHNob3cgcGxvdHMgKGdyYXBocykuCgoKIVtdKFJzdHVkaW9fbGF5b3V0LnBuZykKCgoKCgojIyBSIHNjcmlwdCB2cyBSTUQgKE1hcmtkb3ducyBhbmQgTm90ZWJvb2tzKQoKKiBUbyBzdGFydCBhbiBSIHNjcmlwdCBjbGljayBgY3RybCArIHNoaWZ0ICsgTmAgb3IgYGNtZCArIHNoaWZ0ICsgTmAgb24gbWFjCllvdSBjYW4gcnVuIGxpbmVzIG9mIGNvZGUsIGJ1dCB0aGUgb3V0cHV0IHdvbid0IGJlIHNhdmVkICAgIAoKKiBUbyBzdGFydCBhbiBSTUQgY2xpY2sgYGN0cmwgKyBCL05gIG9yIGBjbWQgKyBCL05gIG9uIG1hYyB0byBpbml0aWFsaXplIGEgbm90ZWJvb2sgIApBbGxvd3MgeW91IHRvIHJ1biBhbmQgdmlzdWFsaXplIG91dHB1dCBpbiB0aGUgZmlsZSwgZWFzeSB0byBzaGFyZSB3aXRoIGNvbGxhYm9yYXRvcnMgc2hvd2luZyBib3RoIHRoZSBjb2RlIGFuZCBhbGwgcmVzdWx0cy9wbG90cyAgCgoKClRoaXMgaXMgY2FsbGVkIHRoZSBtYWluIGNodW5rLCB3aGVyZSBpcyBoYXMgYWxsIHRoZSBzZXR0aW5ncyB0aGF0IHlvdSB3b3VsZCBsaWtlIHRvIGFwcGx5IGFsb25nIHRoZSBub3RlYm9vawpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBULCByZXN1bHRzID0gInNob3ciKQpvcHRpb25zKHdpZHRoPTgwKQpyZXF1aXJlKCJrbml0ciIpCgojIyBUbyBjaGFuZ2UgdGhlIGRpcmVjdG9yeSBpbiBhIG5vdGVib29rL21hcmtkb3duCiMgb3B0c19rbml0JHNldChyb290LmRpciA9ICJwYXRoL3RvL2RpcmVjdG9yeS8iKQpgYGAKCgpUbyBpbnNlcnQgYSBjaHVuaywgdXNlIGBjdHJsICsgYWx0ICsgSWAsIG9yIGBjb21tYW5kICsgb3B0aW9uICsgSWAKCiMgUiBCYXNpY3MKCiMjIFRoZSBjb25zb2xlCk9wZW4gUlN0dWRpbywgY2xpY2sgb24gdGhlICJDb25zb2xlIiBwYW5lbCwgdHlwZSBgMysyYCBhbmQgcHJlc3MgZW50ZXIuIFIgZGlzcGxheXMgdGhlIHJlc3VsdCBvZiB0aGUgY2FsY3VsYXRpb24uIEluIHRoaXMgZG9jdW1lbnQsIHdlIHdpbGwgYmUgc2hvd2luZyBzdWNoIGFuIGludGVyYWN0aW9uIHdpdGggUiBhcyBiZWxvdy4KYGBge3J9CjMgKyAyCmBgYAoKYCtgIGlzIGNhbGxlZCBhbiBvcGVyYXRvci4gUiBoYXMgb3RoZXIgb3BlcmF0b3JzIGZvciBiYXNpYyBtYXRoZW1hdGljYWwgY2FsY3VsYXRpb25zOiAgCmAtYCBmb3Igc3VidHJhY3Rpb24gIApgKmAgZm9yIG11bHRpcGxpY2F0aW9uICAKYC9gIGZvcndhcmQgc2xhc2ggZm9yIGRpdmlzaW9uICAKYF5gIGhhdCBmb3IgInBvd2VyIgoKYCpgIGhhcyBoaWdoZXIgcHJlY2VkZW5jZSB0aGFuIGArYC4gIFdlIGNhbiB1c2UgYnJhY2tldHMgIGAoIClgLCBpZiBuZWNlc3NhcnkuIFRyeSBgMSsyKjNgIGFuZCBgKDErMikqM2AKCmBgYHtyfQoxKzIqMwpgYGAKCmBgYHtyfQooMSsyKSozCmBgYAoKCk1ha2luZyBsaW5lcyBvZiBjb2RlIGFjY29yZGluZyB0byBzdHlsZXIsIGhpZ2hsaWdodCB0aGUgbGluZXMgb2YgY29kZSwgY2xpY2sgYGN0cmwgKyBzaGlmdCArIEFgIG9yIGBjbWQgKyBzaGlmdCArIEFgCgoKIyMgQ29tcGFyaXNvbnMKCldlIHVzZSBgPmAsIGA8YCwgYD49YCwgYD09YCwgYDw9YCwgYCE9YCBmb3IgY29tcGFyaXNvbnMuIE5vdGUgdGhhdCBmb3IgY29tcGFyaXNvbnMgaXRzIGFsd2F5cyBhIGRvdWJsZSBzaWduIHRvIGJlIHVzZWQuIFRoaXMgcmV0dXJucyBhICJsb2dpY2FsIiB2YWx1ZSBvZiBgVFJVRWAgb3IgYEZBTFNFYC4KCmBgYHtyfQozICogMyA9PSA5CmBgYAoKVGhpcyBsaXRlcmFsbHkgbWVhbnMgSXMgdGhlIGxlZnQgaGFuZCBzaWRlIGVxdWFsIHRvIHRoZSByaWdodCBoYW5kIHNpZGU/IElzIDMgdGltZXMgMyBlcXVhbCB0byA5PyAgCgoKIyMgVmFyaWFibGVzCgpBIHZhcmlhYmxlIGlzIGEgbmFtZSB0aGF0IHN0b3JlcyBhIHZhbHVlLiBXZSBjcmVhdGUgYSB2YXJpYWJsZSBieSBhc3NpZ2luaW5nIGEgdmFsdWUgdG8gaXQgdXNpbmcgYDwtYCB3aGljaCBwb2ludHMgdG8gdGhlIGxlZnQgYXNzaWduaW5nIHRoZSB2YWx1ZSBvZiB0aGUgcmlnaHQgdG8gdGhlIG9uZSBvbiB0aGUgbGVmdAoKYGBge3J9CndlaWdodF9rZyA9IDY1CndlaWdodF9rZyA8LSA2NQpgYGAKCgpUbyBwcmludCB0aGUgdmFsdWUgc3RvcmVkIGluIGEgdmFyaWFibGUsIHByaW50IHRoZSB2YXJpYWJsZQpgYGB7cn0Kd2VpZ2h0X2tnCmBgYAoKTmFtZXMgb2YgdGhlIHZhcmlhYmxlcyBzaG91bGQgbm90IGNvbnRhaW4gc3BlY2lhbCBjaGFyYWN0ZXJzIG9yIHNwYWNlcywgc2hvdWxkIG5vdCBzdGFydCB3aXRoIGEgbnVtYmVyLiBEb3RzIGFyZSBvaywgdW5saWtlIG1hbnkgb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzCgoKVG8gbGlzdCBhbGwgdmFyaWFibGVzIGluIGFuIGVudmlyb25tZW50IApgYGB7cn0KbHMoKQojIyB0byByZW1vdmUgYSB2YXJpYWJsZS9vYmplY3QKcm0od2VpZ2h0X2tnKQoKIyMgdG8gcmVtb3ZlIHNvbWUgdmFyaWFibGUgb2YgYSBjZXJ0YWluIHBhdHRlcm4gKHN0YXJ0aW5nL2VuZGluZy9pbmNsdWRpbmcgLi4uLikKcm0obGlzdCA9IGxzKHBhdHRlcm4gPSAiIikpCmBgYAoKCgoKQW55IGxpbmUgb2YgY29kZSBpbnNpZGUgYSBjaHVuayBvciBpbiB0aGUgY29uc29sZSB0aGF0IGhhcyBhIGAjYCBiZWZvcmUsIGlzIGEgY29tbWVudC4gVXNlZnVsIHRvIGRvY3VtZW50IHlvdXIgY29kZSBzbyBpdHMgZWFzeSB0byByZW1lbWJlciB3aHkgd2UgdXNlZCB0aGlzIGNvbW1hbmQKClRvIGFkZCBhIGAjYCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBsaW5lIGZvciBtdWx0aXBsZSBsaW5lcywgaGlnaGxpZ2h0IGFsbCBsaW5lcywgYW5kIGNsaWNrIGBjdHJsICsgc2hpZnQgKyBjYCBvciBgY21kICsgc2hpZnQgKyBjYAoKV2UgY2FuIGRvIGFyaXRoZW10aWMgb24gdGhlIHZhcmlhYmxlCmBgYHtyfQp3ZWlnaHRfa2cgPC0gNjUKMi4yICogd2VpZ2h0X2tnCmBgYAoKCiMjIFZlY3RvcnMKQSB2ZWN0b3IgaXMgYSBjb2xsZWN0aW9uIG9mIGVsZW1lbnRzIChudW1iZXJzLCB3b3JkcywuLiBldGMpClRvIGJlIGFibGUgdG8gY29tYmluZSBlbGVtZW50cyBpbnRvIGEgdmVjdG9yLCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGMoKSB3aGljaCBzdGFuZHMgZm9yIGNvbWJpbmUgb3IgY29uY2F0ZW5hdGUKCmBgYHtyfQpjKDEsMiwzLDQpCmBgYAoKCmBgYHtyfQp2ZWMgPC0gYygxMCwyMCwzMCw0MCkKdmVjICsgMSAjIEhlcmUgdGhlIGFyaXRobWV0aWMgKyAxIGlzIGFwcGxpZWQgb24gZXZlcnkgZWxlbWVudCBvZiB0aGUgdmVjdG9yCmBgYAoKCldlIGNhbiBhbHNvIGNvbWJpbmUgdmVjdG9ycwpgYGB7cn0KdmVjICsgdmVjIAojIEluIHRoaXMgY2FzZSwgdGhlIDFzdCBlbGVtZW50IG9mIHZlYyBpcyBhZGRlZCB0byB0aGUgMXN0IGVsZW1lbnQgb2YgdmVjLCAybmQgd2l0aCAybmQuLiBhbmQgc28gb24KYGBgCgpUbyBjaGVjayB0aGUgbGVuZ3RoIG9mIHRoZSB2ZWN0b3Igd2UgdXNlIHRoZSBmdW5jdGlvbiBsZW5ndGgoKQpgYGB7cn0KbGVuZ3RoKHZlYykKYGBgCgojIyBEYXRhIHR5cGVzIGluIFIKCk1vc3QgY29tbW9uIGRhdGEgdHlwZXMgaW4gUiBhcmUgbWFpbmx5OiAgCiogTnVtZXJpYyAoY291bGQgYmUgZG91YmxlIHdoaWNoIGlzIHRoZSBkZWZhdWx0LCBvciBpbnRlZ2VyKSAgCiogQ2hhcmFjdGVyIChTdHJpbmcpICAKKiBMb2dpY2FsIChUUlVFL0ZBTFNFKSAgCgpUbyBjaGVjayB0aGUgdHlwZSBvZiBhIHZlY3Rvciwgd2UgdXNlIHR5cGVvZigpIGZ1bmN0aW9uCmBgYHtyfQp4IDwtIDUKdHlwZW9mKHgpCgojIyBUbyBjcmVhdGUgYW4gaW50ZWdlciBhZGQgTCB0byB0aGUgbnVtYmVyCnggPC0gNUwKdHlwZW9mKHgpCnByaW50KHgpCmNsYXNzKHgpCmBgYAoKRm9yIENoYXJhY3RlcnMsIHdlIHVzZSBzaW5nbGUgb3IgZG91YmxlIHF1b3RlcyAKCmBgYHtyfQoiSGVsbG8gV29ybGQiCnBsYWluX3R4dCA9ICdUaGlzIGlzIGEgc3RyaW5nJwpwcmludChwbGFpbl90eHQpCnR5cGVvZihwbGFpbl90eHQpCmNsYXNzKHBsYWluX3R4dCkKYGBgCgpUbyBiZSBhYmxlIHRvIGxvb2sgZm9yIGFuZCBtb2RpZnkgYSBwYXR0ZXJuLCB0aGUgdmVjdG9yIHNob3VsZCBiZSBhIHN0cmluZyEKClRoZXJlIGFyZSBhbHNvIGNhdGVnb3JpY2FsIHZlY3RvcnMgaW4gd2hpY2ggZWxlbWVudHMgY2FuIGJlIG9uZSBvZiBzZXZlcmFsICJjYXRlZ29yaWVzL2xldmVscyIKYGBge3J9CmZhY3RvcihjKCJtdXRhbnQiLCAid2lsZHR5cGUiLCAibXV0YW50IiksIAogICAgICAgbGV2ZWxzID0gYygid2lsZHR5cGUiLCAibXV0YW50IikpCmBgYAoKRmFjdG9yIGlzIGltcG9ydGFudCBmb3IgYXJyYW5nZW1lbnQgb2YgZGF0YSBpbiBhIHBsb3QgZm9yIGV4YW1wbGUuCgoKCiMjIEFjY2Vzc2luZyBlbGVtZW50cyBvZiB2ZWN0b3JzIChJbmRleGluZykKVG8gYWNjZXNzIGVsZW1lbnRzLCB3ZSB1c2UgW10gd2l0aCBhIG51bWJlciAoaW5kZXgpCmBgYHtyfQp2ZWNbMV0KdmVjWy0xXQp2ZWNbYygxLDMpXQp2ZWNbMTo0XQp2ZWNbMV0gPC0gNgpgYGAKCldlIGNhbiB1c2UgYSB2ZWN0b3IgaW5zdGVhZCBvZiBudW1iZXJzIHRvIGluZGV4IGEgdmVjdG9yIApgYGB7cn0KbXlpbmRleCA8LSBjKDQsMiwxLDMpCnZlY1tteWluZGV4XQoKIyMgd2hpY2ggaXMgdGhlIHNhbWUgYXMKdmVjW2MoNCwyLDEsMyldCmBgYAoKIyMgTWF0cml4CjItRCB0YWJ1bGFyIGRhdGEgc3RydWN0dXJlIGluIHdoaWNoIGFsbCBlbGVtZW50cyBhcmUgb2YgdGhlIHNhbWUgdHlwZSAodHlwaWNhbGx5IG51bWVyaWMgbWF0cmljZXMsIGJ1dCBjb3VsZCBhbHNvIGJlIGNoYXJhY3RlciBvciBsb2dpY2FsKQpgYGB7cn0KIyBUbyBjcmVhdGUgYSBtYXRyaXgKbWF0cml4KCkKIyBFeGFtcGxlOgptYXQgPC0gbWF0cml4KDE6MTIsIG5yb3cgPSAyLCBuY29sID0gNiwgYnlyb3cgPSBGKQptYXQKdHlwZW9mKG1hdCkKY2xhc3MobWF0KQpgYGAKCmBgYHtyfQojIFRvIGdldCB0aGUgZGltZW5zaW9ucyBvZiBhIG1hdHJpeApkaW0obWF0KQojIyBnZXQgb25seSBob3cgbWFueSByb3dzCm5yb3cobWF0KQojIyBnZXQgb25seSBob3cgbWFueSBjb2x1bW5zCm5jb2wobWF0KQojIyBnZXQgY29sdW1uIG5hbWVzCmNvbG5hbWVzKG1hdCkKIyMgQWNjZXNzaW5nIGVsZW1lbnRzIG9mIGEgbWF0cml4Cm1hdFsxLDNdICMgcm93IG51bWJlciwgIyBjb2wgbnVtYmVyXQojIHRoaXMgcmV0dW5zIHRoZSBlbGVtZW50IGF0IHJvdyAxLCBjb2x1bW4gMwpgYGAKCiMjIERhdGEgZnJhbWUKU2ltaWxhciB0byBhIG1hdHJpeCwgdGhlIGRpZmZlcmVuY2UgaXMgdGhhdCB0aGUgY29sdW1ucyBvZiBhIGRhdGFmcmFtZSBjb3VsZCBiZSBvZiBkaWZmZXJlbnQgdHlwZXMgKGNvdWxkIGNvbWJpbmUgbnVtZXJpYyB2YWx1ZXMsIHN0cmluZ3MsIGxvZ2ljYWwpClRoZSBpbmRleCBvZiBlYWNoIHJvdyBjb3VsZCBiZSBmcm9tIDEgdG8gbGVuZ3RoKGRhdGFmcmFtZSkgb3IgY291bGQgaGF2ZSBhIHVuaXF1ZSBuYW1lIChjYW4gYmUgYWNjZXNzZWQvbW9kaWZpZWQgdXNpbmcgcm93bmFtZXMgZnVuY3Rpb24pCgojIyBGdW5jdGlvbnMKYSBmdW5jdGlvbiBpcyBmb2xsb3dlZCBieSBwYXJlbnRoZXNlcyAoKQptZWFuKCksIHNkKCksIHByaW50KCksIHJlcCgpCmBgYHtyfQojIFRvIGNyZWF0ZSBhIGZ1bmN0aW9uCm5hbWVfb2ZfZnVuY3Rpb24gPC0gZnVuY3Rpb24oKXsgIyBpbnNpZGUgcGFyZW50aGVzZXMgd2hhdCB0aGUgZnVuY3Rpb24gdGFrZXMKICAjIHdoYXQgdGhlIGZ1bmN0aW9uIHNob3VsZCBkbwp9CgojIyB0aGVuIGNhbGwgdGhlIGZ1bmN0aW9uCm5hbWVfb2ZfZnVuY3Rpb24oKQpgYGAKCgpUaGUgZnVuY3Rpb24gdGFrZXMgYXJndW1lbnRzIChvcHRpb25zKSwgc29tZSBhcmUgbWFuZGF0b3J5IGFuZCBzb21lIGFyZSBvcHRpb25hbC4KQXJndW1lbnRzIGNhbiBiZSBzdXBwbGllZCBpbiBvcmRlciAocG9zaXRpb25hbCBhcmd1bWVudCksIG9yIGJ5IHRoZSBhcmd1bWVudCBuYW1lIChiZXR0ZXIgdG8gdXNlKS4KQ2xpY2tpbmcgYHRhYmAgZm9yIGF1dG9jb21wbGV0aW9uIGhlbHBzIHVzIHJlbWVtYmVyIHdoYXQgYXJndW1lbnRzIGEgZnVuY3Rpb24gY291bGQgdGFrZQoKYGBge3J9CmNhbGMgPC0gZnVuY3Rpb24obnVtMSwgbnVtMil7CiAgc3VtID0gbnVtMSArIG51bTIKICBwcmludChzdW0pCn0KCmNhbGMobnVtMSA9IDEwLCA1KQpgYGAKCgoKIyMgTGlzdHMKCkxpc3RzIGNhbiBjb250YWluIGRpZmZlcmVudCBraW5kcyBvZiBlbGVtZW50cwpgYGB7cn0KbXlsaXN0IDwtIGxpc3QobnVtPSA0MiwgZ3JlZXRpbmc9IkhlbGxvLCBXb3JsZCIpCgojIHRvIGFjY2VzcyBlbGVtZW50czogIyMgZG91YmxlIHNxdWFyZSBicmFja2V0IHRvIGFjY2VzcwpteWxpc3RbWzFdXQoKbXlsaXN0JGdyZWV0aW5nCgpgYGAKCklmIHlvdSBhcmUgbm90IHN1cmUgYWJvdXQgdGhlIHR5cGUgb2YgYSB2YXJpYWJsZSwgcnVuIGNsYXNzCmBgYHtyfQpjbGFzcyh2ZWMpCmNsYXNzKG1hdCkKc3RyKG15bGlzdCkKYGBgCgojIyMgQXBwbHlpbmcgYSBmdW5jdGlvbiBvbiBhIGxpc3QgKHZlcnkgcHJhY3RpY2FsKQpgYGB7cn0KbGlzdF9udW1zIDwtIGxpc3QoCiAgYSA9IGMoMSwgMiwgMyksCiAgYiA9IGMoMTAsIDIwLCAzMCksCiAgYyA9IGMoNSwgNSwgNSkKKQoKbGFwcGx5KGxpc3RfbnVtcywgbWVhbikKYGBgCgojIyMgQXBwbHlpbmcgYW4gYW5vbnltb3VzIGZ1bmN0aW9uICh3aGVuIHdlIHdhbnQgdG8gcGVyZm9ybSBtYW55IHN0ZXBzIG92ZXIgdGhlIGxpc3QpCmBgYHtyfQpsaXN0X251bXNfbW9kaWZpZWQgPC0gbGFwcGx5KGxpc3RfbnVtcywgZnVuY3Rpb24oZWxlbWVudCl7CiAgIyMgZmlyc3QgZG8gdGhpcyBvbiBlYWNoIGVsZW1lbnQKICBlbGVtZW50ICsgNAogICMjIHRoZW4gZG8gdGhpcwogIHJldHVybihlbGVtZW50KQp9KQpgYGAKCgpgYGB7cn0KIyMgQXBwbHlpbmcgdGhlIG1lYW4gZnVuY3Rpb24gb3ZlciBhIGxpc3QKbGFwcGx5KGxpc3RfbnVtcywgZnVuY3Rpb24oZWxlbSkgZWxlbV4yKSAjIyByZXR1cm5zIGEgbGlzdApgYGAKCgojIyMgc2FwcGx5IChzaW1wbGUgYXBwbHkpCmZvciBhIHNpbXBsaWZpZWQgdmVyc2lvbgpgYGB7cn0Kc2FwcGx5KGxpc3RfbnVtcywgZnVuY3Rpb24oZWxlbSkgZWxlbV4yKSAjIyByZXR1cm5zIGEgc2ltcGxpZmllZCBmb3JtICh2ZWN0b3IpCmBgYAoKCgojIyBMb29wcwoKYGBge3J9CmZvciAobnVtYiBpbiBsaXN0X251bXMpewogIHByaW50KG51bWIpCn0KYGBgCgojIyBJZiBjb25kaXRpb25hbApgYGB7cn0KbGlzdF9udW1zIDwtIGMoMSwyLDMsNiw3KQpmb3IobnVtYiBpbiBsaXN0X251bXMpewogIGlmKG51bWIgPiA1KXsKICAgIHByaW50KG51bWIpfQogICAgZWxzZSBwcmludCgiVGhpcyBudW1iZXIgaXMgc21hbGxlciB0aGFuIDUsIHNvIHdpbGwgbm90IGJlIHByaW50ZWQiKQp9CmBgYAoKCgojIFRpZHkgZGF0YQoKClRpZHkgZGF0YSBydWxlczoKMS4gRWFjaCB2YXJpYWJsZSA9IG9uZSBjb2x1bW4KMi4gRWFjaCBvYnNlcnZhdGlvbiA9IG9uZSByb3cKMy4gRWFjaCB0eXBlIG9mIGVudGl0eSA9IG9uZSB0YWJsZQoKV2h5IGl0IG1hdHRlcnM6Ci0gZWFzaWVyIHBsb3R0aW5nCi0gZWFzaWVyIG1vZGVsbGluZwotIGZld2VyIGJ1Z3MKLSBjb21wYXRpYmxlIHdpdGggdGlkeXZlcnNlIGdyYW1tYXIKCgoKIyMgSW5zdGFsbGluZyBwYWNrYWdlcwoKVG8gaW5zdGFsbCBhIHBhY2thZ2UgZnJvbSBbQ1JBTl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvKSwgd2UgdXNlIHRoZSBmdW5jdGlvbiBpbnN0YWxsLnBhY2thZ2VzIApgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygic3R5bGVyIikKaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKYGBgCgojIyMgRnJvbSBHaXRodWIKCltHaXRodWJdKGh0dHBzOi8vZ2l0aHViLmNvbSkgaXMgYSB3ZWItYmFzZWQgcGxhdGZvcm0gZm9yIHZlcnNpb24gY29udHJvbCwgY29sbGFib3JhdGlvbiwgYW5kIGNvZGUgc2hhcmluZywgYnVpbHQgYXJvdW5kIHRoZSBHaXQgc3lzdGVtLiBJdCBhbGxvd3MgdXNlcnMgdG8gdHJhY2sgY2hhbmdlcyB0byBmaWxlcywgbWFuYWdlIGNvbnRyaWJ1dGlvbnMgZnJvbSBtdWx0aXBsZSBwZW9wbGUsIGFuZCBwdWJsaXNoIG9wZW4tc291cmNlIHByb2plY3RzLiAgCgpHaXRIdWIgaXMgd2lkZWx5IHVzZWQgZm9yOgoKLSBTaGFyaW5nIFIgcGFja2FnZXMgYmVmb3JlIHRoZXkgYXJlIHB1Ymxpc2hlZCBvbiBDUkFOCgotIERldmVsb3BpbmcgcGFja2FnZXMgY29sbGFib3JhdGl2ZWx5IHVzaW5nIHB1bGwgcmVxdWVzdHMgYW5kIGlzc3VlcwoKLSBTdG9yaW5nIGFuYWx5c2lzIHByb2plY3RzIChzY3JpcHRzLCBSIE1hcmtkb3duLCBkYXRhLCBkb2N1bWVudGF0aW9uKQoKLSBSZXByb2R1Y2libGUgd29ya2Zsb3dzLCBzaW5jZSBldmVyeSBjaGFuZ2UgaXMgdHJhY2tlZAoKCkluc3RhbGxpbmcgYSBwYWNrYWdlIGZyb20gR2l0aHViIGBpbnN0YWxsX2dpdGh1YigiYXV0aG9yL3BhY2thZ2UiKWAKCgohW10oR2l0aHViX3NjcmVlbnNob3RfMS5wbmcpCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQoKIyMgVG8gbG9hZCBhIGxpYnJhcnkKbGlicmFyeShkZXZ0b29scykKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJnaXRodWJ1c2VybmFtZS9yZXBvc2l0b3J5IikKaW5zdGFsbF9naXRodWIoImltbXVub2dlbm9taWNzL3ByZXN0byIpCgojIyBpbnN0ZWFkIG9mIGxvYWRpbmcgYSB3aG9sZSBwYWNrYWdlLCB3ZSBjYW4gY2FsbCBhIGZ1bmN0aW9uIGZyb20gYSBwYWNrYWdlIHVzaW5nIHRoZSBmb3JtYXQ6CnBhY2thZ2U6OmZ1bmN0aW9uKCkKICAKIyMgZnVuY3Rpb24gc2VsZWN0CkFubm90YXRpb25EYmk6OnNlbGVjdCgpCmRwbHlyOjpzZWxlY3QoKQogIAojIyBFeGFtcGxlCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaW1tdW5vZ2Vub21pY3MvcHJlc3RvIikKYGBgCgoKIyMjIEZyb20gQmlvY29uZHVjdG9yCltCaW9jb25kdWN0b3JdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvKQoKCkJpb2NvbmR1Y3RvciBpcyBhbiBvcGVuLXNvdXJjZSBwcm9qZWN0IHRoYXQgcHJvdmlkZXMgYSBsYXJnZSBjb2xsZWN0aW9uIG9mIFIgcGFja2FnZXMgZm9yIGJpb2xvZ2ljYWwgYW5kIGJpb21lZGljYWwgZGF0YSBhbmFseXNpcywgZXNwZWNpYWxseSBoaWdoLXRocm91Z2hwdXQgc2VxdWVuY2luZywgZ2Vub21pY3MsIHRyYW5zY3JpcHRvbWljcywgYW5kIHByb3Rlb21pY3MuCgotIEN1cmF0ZWQsIHBlZXItcmV2aWV3ZWQgcGFja2FnZXM6IEV2ZXJ5IHBhY2thZ2UgZ29lcyB0aHJvdWdoIGEgc3RydWN0dXJlZCByZXZpZXcgZm9yIGNvZGUgcXVhbGl0eSwgZG9jdW1lbnRhdGlvbiwgYW5kIHJlcHJvZHVjaWJpbGl0eS4KCi0gRm9jdXMgb24gbGlmZSBzY2llbmNlczogVG9vbHMgZm9yIFJOQS1zZXEsIENoSVAtc2VxLCBzaW5nbGUtY2VsbCBSTkEtc2VxLCBlcGlnZW5vbWljcywgdmFyaWFudCBhbmFseXNpcywgYW5ub3RhdGlvbiwgYW5kIG1vcmUuCgotIFJlcHJvZHVjaWJsZSB3b3JrZmxvd3MgdXNpbmcgc3RhbmRhcmRpemVkIGRhdGEgc3RydWN0dXJlcyAoZS5nLiwgU3VtbWFyaXplZEV4cGVyaW1lbnQsIFNpbmdsZUNlbGxFeHBlcmltZW50KS4KCi0gRnJlcXVlbnQgcmVsZWFzZSBjeWNsZSAodHdpY2UgeWVhcmx5KSwgc3luY2hyb25pemVkIHdpdGggUiB2ZXJzaW9ucy4KCkJpb2NvbmR1Y3RvciBoYXMgYmVjb21lIGEgY2VudHJhbCBlY29zeXN0ZW0gZm9yIGNvbXB1dGF0aW9uYWwgYmlvbG9neSBiZWNhdXNlIGl0IHByb21vdGVzIHJlcHJvZHVjaWJpbGl0eSwgaW50ZWdyYXRlcyB3aXRoIG1vZGVybiBSIHdvcmtmbG93cywgYW5kIHByb3ZpZGVzIGNvbnNpc3RlbnQgaW50ZXJmYWNlcyBmb3IgY29tcGxleCBiaW9sb2dpY2FsIGRhdGFzZXRzLgoKCgohW10oQmlvY29uZHVjdG9yX3NjcmVlbjEucG5nKQoKIVtdKEJpb2NvbmR1Y3Rvcl9zY3JlZW4yLnBuZykgIAoKYGBge3J9CiMjIEZpcnN0LCB0byBpbnN0YWxsIEJpb2NvbmR1Y3RvcgppZiAoIXJlcXVpcmUoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQpCaW9jTWFuYWdlcjo6aW5zdGFsbCh2ZXJzaW9uID0gIjMuMjIiKQoKIyMgSW5zdGFsbGluZyBwYWNrYWdlcyBmcm9tIEJpb2NvbmR1Y3RvcgpCaW9jTWFuYWdlcjo6aW5zdGFsbCgiREVTZXEyIikKYGBgCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKaW5zdGFsbC5wYWNrYWdlcygiamFuaXRvciIpCmluc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpCmBgYAoKIyMgTG9hZGluZyBsaWJyYXJpZXMKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShsdWJyaWRhdGUpCgojIFRvIGxvYWQgbXVsdGlwbGUgbGlicmFyaWVzIGF0IG9uY2UsIHdlIGNhbiB1c2UgcGFjbWFuIHBhY2thZ2UsIHBfbG9hZCBmdW5jdGlvbgpwYWNtYW46OnBfbG9hZCh0aWR5dmVyc2UsIHN0eWxlciwgZHBseXIsIHN0cmluZ3IsIGphbml0b3IsIGx1YnJpZGF0ZSkKCmBgYAoKCgojIyBXb3JraW5nIHdpdGggZGlyZWN0b3JpZXMKYGBge3J9CiMjIFRvIGNoZWNrIHRoZSBjdXJyZW50IGRpcmVjdG9yeQpnZXR3ZCgpCiMjIFRvIGNoYW5nZSB0aGUgd29ya2luZyBkaXJlY3RvcnkKc2V0d2QoIlBhdGgvdG8vZGlyZWN0b3J5LyIpCgojIyBSZW1lbWJlciB0byBjaGFuZ2UgdGhlIGRpcmVjdG9yeSBmb3IgdGhlIHdob2xlIG5vdGVib29rLCB3ZSB1c2UgdGhlIG1haW4gY2h1bmsKYGBgCgojIyBJbXBvcnRpbmcgZGF0YQpgYGB7cn0KIyMgSW1wb3J0aW5nIGFuIFIgZGF0YSBzdHJ1Y3R1cmUgb2JqZWN0ICguUkRTKQpteWZpbGUgPC0gcmVhZFJEUyhmaWxlID0gInBhdGgvdG8vZmlsZS5SRFMiKQoKIyMgQ1NWIGZpbGUKbXlmaWxlIDwtIHV0aWxzOjpyZWFkLmNzdigpCgojIyBNb3JlIGdlbmVyYWwgZnVuY3Rpb24KbXlmaWxlIDwtIHV0aWxzOjpyZWFkLmRlbGltKGZpbGUgPSAiL3BhdGgvdG8vZmlsZS5leHRlbnN0aW9uIiwgCiAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsIHNlcCA9ICJcdCIpICMjIHRoZSBmaWxlIGhhcyBhIGhlYWRlciB3aXRoIGNvbHVtbiBuYW1lcywgYW5kIGVhY2ggb2JzZXJ2YXRpb24gaXMgc2VwYXJhdGVkIGJ5IGEgdGFiCm15ZmlsZSA8LSByZWFkeGw6OnJlYWRfeGxzeCgiL3BhdGgvdG8vZmlsZS9maWxlLnhsc3giKQpgYGAKCgojIyBDaGVja2luZyB0aGUgY29udGVudCBvZiBhIGZpbGUKYGBge3J9CmhlYWQobXlmaWxlKSAjIyBzaG93cyB0aGUgZmlyc3QgNiBvYnNlcnZhdGlvbnMsIGNhbiBjaGFuZ2UgbiBhcmd1bWVudAp0YWlsKG15ZmlsZSkgIyMgc2hvd3MgdGhlIGxhc3QgNiBvYnNlcnZhdGlvbnMsIGNhbiBjaGFuZ2UgbiBhcmd1bWVudApzdHIobXlmaWxlKSAjIyBjaGVja3MgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZmlsZSBhcyBhIHZhcmlhYmxlCmRpbShteWZpbGUpICMjIGNoZWNrcyB0aGUgZGltZW5zaW9ucyBvZiBhIGZpbGUgKHJldHVybnMgbnVtYmVyIG9mIHJvd3MsIG51bWJlciBvZiBjb2x1bW5zKQpucm93KG15ZmlsZSkgIyBjaGVja3MgdGhlIG51bWJlciBvZiByb3dzCm5jb2wobXlmaWxlKSMgY2hlY2tzIHRoZSBudW1iZXIgb2YgY29sdW1ucwoKIyMgY2hlY2tzIHRoZSBjb2x1bW4gbmFtZXMKY29sbmFtZXMobXlmaWxlKQpuYW1lcyhteWZpbGUpCmBgYAoKIyMgRGF0YSBtYW5pdXBsYXRpb24KCiMjIyBBZGRpbmcgY29sdW1ucyB0byBhIGRhdGFmcmFtZQpgYGB7cn0KbXlmaWxlJG5ld2NvbCA8LSBteWZpbGUkY29sMSAqIG15ZmlsZSRjb2wyCmBgYAoKIyMjIEFjY2VzcyBjb2x1bW5zIGJ5IG5hbWUKYGBge3J9CiMgb25lIGNvbHVtbgpteWZpbGUkY29sdW1ubmFtZQojIyBtb3JlIHRoYW4gYSBjb2x1bW4KbXlmaWxlWywgYygiY29sMSIsICJjb2w0IiwgImNvbDIiKV0KbXlmaWxlWywgYygxLDQsMildCmBgYAoKIyMjIEFjY2VzcyBhIGNvbHVtbiBieSBpbmRleApgYGB7cn0KbXlmaWxlWyw0XSAjIHRoZSByb3cgaXMgZW1wdHksIHdoaWNoIG1lYW5zIHNob3cgYWxsIHJvd3MgZm9yIGNvbHVtbiA0CmBgYAoKIyMjIEFjY2VzcyByb3dzCmBgYHtyfQpkZls0LCBdICMjIGdldHMgdGhlIDR0aCByb3cgZm9yIGFsbCBjb2x1bW5zCmRmWzQ6MTBdICMjIGZyb20gNHRoIHRvIDEwdGgKYGBgCgojIyMgU2VsZWN0IHJvd3MgYnkgYSBjb25kaXRpb24gKGV4YWN0KQpgYGB7cn0KbXlmaWxlX3N1YnNldCA8LSBzdWJzZXQobXlmaWxlLCBnaXZlbl9jb2x1bW4gPT0gInNvbWV0aGluZyIpICMjIHN1YnNldCByb3dzIHRoYXQgZm9sbG93IHRoaXMgY29uZGl0aW9uCmBgYAoKCiMjIyBTZWxlY3Qgcm93cyBieSBhIGNvbmRpdGlvbiAobW9yZSB0aGFuIG9uZSBjYXRlZ29yeSkKYGBge3J9Cm15ZmlsZV9zdWJzZXQgPC0gc3Vic2V0KG15ZmlsZSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGdpdmVuX2NvbHVtbiAlaW4lIGMoInNvbWV0aGluZyIsICJzb21ldGhpbmcgZWxzZSIpKSAjIyBzdWJzZXQgcm93cyB0aGF0IGZvbGxvdyB0aGlzIGNvbmRpdGlvbgpgYGAKCgojIyMgU2VsZWN0IHJvd3MgYnkgYSBjb25kaXRpb24gKGV4Y2x1c2lvbiBvZiBleGFjdCkKYGBge3J9Cm15ZmlsZV9zdWJzZXQgPC0gc3Vic2V0KG15ZmlsZSwgZ2l2ZW5fY29sdW1uICE9ICJzb21ldGhpbmciKSAjIyBzdWJzZXQgcm93cyB0aGF0IGZvbGxvdyB0aGlzIGNvbmRpdGlvbgpgYGAKCiMjIyBTZWxlY3Qgcm93cyBieSBhIGNvbmRpdGlvbiAoZXhjbHVzaW9uIG9mIG1vcmUgdGhhbiBvbmUgY2F0ZWdvcnkpCgpgYGB7cn0KbXlmaWxlX3N1YnNldCA8LSBzdWJzZXQobXlmaWxlLCBnaXZlbl9jb2x1bW4gIT0gInNvbWV0aGluZyIgJiAKICAgICAgICAgZ2l2ZW5fY29sdW1uICE9ICJzb21ldGhpbmcgZWxzZSIgJgogICAgICAgICBnaXZlbl9jb2x1bW4gIT0gImFub3RoZXIgdGhpbmciKQpgYGAKClVzaW5nIHRoZSBgIWAgdG8gbmVnYXRlIApgYGB7cn0KbXlmaWxlX3N1YnNldCA8LSBzdWJzZXQobXlmaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAhKGdpdmVuX2NvbHVtbiAlaW4lIGMoInNvbWV0aGluZyIsICJzb21ldGhpbmcgZWxzZSIpKSkKYGBgCgoKClRoZXJlIGlzIG5vIGVxdWl2YWxlbnQgb3BlcmF0b3IgZm9yIGAlaW4lYCwgYnV0IHlvdSBjYW4gY3JlYXRlIGl0ISAgCgpgYGB7cn0KIiVvdXQlIiA8LSBOZWdhdGUoIiVpbiUiKQpgYGAKCgpgYGB7cn0KbXlmaWxlX3N1YnNldCA8LSBzdWJzZXQobXlmaWxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgZ2l2ZW5fY29sdW1uICVvdXQlIGMoInNvbWV0aGluZyIsICJzb21ldGhpbmcgZWxzZSIpKSAKIyMgc3Vic2V0IHJvd3MgdGhhdCBkbyBub3QgZm9sbG93IHRoaXMgY29uZGl0aW9uCmBgYAoKCmBgYHtyfQojIGNsZWFuIGNvbHVtbiBuYW1lcwojIGRmIDwtIGphbml0b3I6OmNsZWFuX25hbWVzKGRmKQoKIyBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dHVlc2RheVIiKQojIHR1ZXNkYXRhIDwtIHRpZHl0dWVzZGF5Ujo6dHRfbG9hZCgnMjAyMy0xMC0yNCcpCiMgZGYgPC0gdHVlc2RhdGEkcGF0aWVudF9yaXNrX3Byb2ZpbGVzCgpkZiA8LSByZWFkeGw6OnJlYWRfeGxzeCgidHVlc2RhdGFfZGYueGxzeCIpCmhlYWQoZGYpCmBgYAoKYGBge3J9CmRmJGBwcmVkaWN0ZWQgcmlzayBvZiBTdWRkZW4gVmlzaW9uIExvc3MsIHdpdGggbm8gZXllIHBhdGhvbG9neSBjYXVzZXNgCmBgYAoKCmBgYHtyfQpkZiA8LSBqYW5pdG9yOjpjbGVhbl9uYW1lcyhkZikKaGVhZChkZikKYGBgCgoKCiMgVXNpbmcgdGlkeXZlcnNlClVzZWQgZm9yIGRhdGEgd3JhbmdsaW5nIGFuZCBtYW5pdXBsYXRpb24KCldoZW4gd2UgbG9hZCB0aWR5dmVyc2UsIGJ5IGRlZmF1bHQsIGRwbHlyLCByZWFkciwgc3RyaW5nciwgdGliYmxlLCB0aWR5ciwgcHVycnIsIGFuZCBnZ3Bsb3QyIGFyZSBsb2FkZWQKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCldlIHdpbGwgd29yayB3aXRoIFtjYXJlIHN0YXRlXShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L3RyZWUvbWFpbi9kYXRhLzIwMjUvMjAyNS0wNC0wOCkgZGF0YXNldCBmcm9tIHRpZHkgdHVlc2RheQpgYGB7cn0KY2FyZV9zdGF0ZSA8LSByZWFkcjo6cmVhZF9jc3YoCiAgImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFpbi9kYXRhLzIwMjUvMjAyNS0wNC0wOC9jYXJlX3N0YXRlLmNzdiIKKQoKY2FyZV9zdGF0ZSAlPiUgZ2xpbXBzZSgpCgpgYGAKCgotICpzdGF0ZSogPj4gVGhlIHR3by1sZXR0ZXIgY29kZSBmb3IgdGhlIHN0YXRlIChvciB0ZXJyaXRvcnksIGV0Yykgd2hlcmUgdGhlIGhvc3BpdGFsIGlzIGxvY2F0ZWQuICAKLSAqY29uZGl0aW9uKiA+PglUaGUgY29uZGl0aW9uIGZvciB3aGljaCB0aGUgcGF0aWVudCB3YXMgYWRtaXR0ZWQuIFNpeCBjYXRlZ29yaWVzIG9mIGNvbmRpdGlvbnMgYXJlIGluY2x1ZGVkIGluIHRoZSBkYXRhLiAgCi0gKm1lYXN1cmVfaWQqID4+CVRoZSBJRCBvZiB0aGUgdGhpbmcgYmVpbmcgbWVhc3VyZWQuIE5vdGUgdGhhdCB0aGVyZSBhcmUgMjIgdW5pcXVlIElEcyBidXQgb25seSAyMSB1bmlxdWUgbmFtZXMuICAKLSAqbWVhc3VyZV9uYW1lKiA+PglUaGUgbmFtZSBvZiB0aGUgdGhpbmcgYmVpbmcgbWVhc3VyZWQuIE5vdGUgdGhhdCB0aGVyZSBhcmUgMjIgdW5pcXVlIElEcyBidXQgb25seSAyMSB1bmlxdWUgbmFtZXMuICAKLSAqc2NvcmUqID4+CVRoZSBzY29yZSBvZiB0aGUgbWVhc3VyZS4gIAotICpmb290bm90ZSogCT4+IAlGb290bm90ZXMgdGhhdCBhcHBseSB0byB0aGlzIG1lYXN1cmU6IAogIC0tIDUgPSAiUmVzdWx0cyBhcmUgbm90IGF2YWlsYWJsZSBmb3IgdGhpcyByZXBvcnRpbmcgcGVyaW9kLiIsIAogIC0tIDI1ID0gIlN0YXRlIGFuZCBuYXRpb25hbCBhdmVyYWdlcyBpbmNsdWRlIFZldGVyYW5zIEhlYWx0aCBBZG1pbmlzdHJhdGlvbiAoVkhBKSBob3NwaXRhbCBkYXRhLiIsIAogIC0tIDI2ID0gIlN0YXRlIGFuZCBuYXRpb25hbCBhdmVyYWdlcyBpbmNsdWRlIERlcGFydG1lbnQgb2YgRGVmZW5zZSAoRG9EKSBob3NwaXRhbCBkYXRhLiIuICAKLSAqc3RhcnRfZGF0ZSogPj4JZGF0ZSAJVGhlIGRhdGUgb24gd2hpY2ggbWVhc3VyZW1lbnQgYmVnYW4gZm9yIHRoaXMgbWVhc3VyZS4gIAotICplbmRfZGF0ZSogPj4JZGF0ZSAJVGhlIGRhdGUgb24gd2hpY2ggbWVhc3VyZW1lbnQgZW5kZWQgZm9yIHRoaXMgbWVhc3VyZS4gIAoKUTE6IEhvdyBtYW55IGNvbHVtbnMgYXJlIHRoZXJlIGluIHRoZSBkYXRhc2V0PyBXaGF0IGNvbHVtbnMgZXhpc3Q/IApRMjogSG93IG1hbnkgb2JzZXJ2YXRpb25zKHJvd3MpPyAKUTM6IEhvdyBtYW55IGRpZmZlcmVudCBzdGF0ZXMgZXhpc3QgaW4gdGhlIGRhdGFzZXQgPwpRNDogV2hhdCBjYXRlZ29yaWVzIGFyZSB0aGVyZSBpbiB0aGUgY29uZGl0aW9uIGNvbHVtbj8KUTU6IFdoYXQgaXMgYmVpbmcgbWVhc3VyZWQgaW4gdGhlICJtZWFzdXJlX25hbWUiIGNvbHVtbj8KUTY6IEhvdyBtYW55IG9ic2VydmF0aW9ucyB3aXRoIGZvb3Rub3RlIDUsIDI1LCBhbmQgMjY/ClE3OiBXaGljaCB5ZWFycyBhcmUgdGhlcmUgaW4gdGhlIHN0YXJ0IGFuZCBlbmQgZGF0ZSBjb2x1bW5zPwoKCgpgYGB7cn0Kc3RhcndhcnMgPC0gZHBseXI6OnN0YXJ3YXJzCmBgYAoKYGBge3J9CmhlYWQoc3RhcndhcnMpCmBgYAoKCmBgYHtyfQp0eXBlb2Yoc3RhcndhcnMpCmNsYXNzKHN0YXJ3YXJzKQpgYGAKCmBgYHtyfQojIyBHZXQgdGhlIG5hbWUgb2YgYWxsIGNvbHVtbnMKY29sbmFtZXMoc3RhcndhcnMpCiMgT3IgCm5hbWVzKHN0YXJ3YXJzKQpgYGAKIyBGdW5jdGlvbnMgb24gcm93cwojIyAxLiBGaWx0ZXJpbmcKCmBmaWx0ZXIoZGF0YWZyYW1lLCBjb25kaXRpb24pYCBsaWtlIGBmaWx0ZXIoZGF0YWZyYW1lLCBjb2x1bW4gPT0gInNvbWV0aGluZyIpYApgZmlsdGVyKGRhdGFmcmFtZSwgY29uZGl0aW9uMSwgY29uZGl0aW9uMilgIGxpa2UgYGZpbHRlcihkYXRhZnJhbWUsIGNvbHVtbiA9PSAic29tZXRoaW5nIiwgY29sdW1uID09ICJzb21ldGhpbmciKWAKCgoqKldoeSBpdCBtYXR0ZXJzIGluIGhlYWx0aGNhcmUvYmlvaW5mb3JtYXRpY3M6KiogeW91IGFsbW9zdCBhbHdheXMgZmlsdGVyOgotIFFDIGZhaWx1cmVzCi0gbm9uLXRhcmdldCB0aXNzdWUgLyBjb25kaXRpb24KLSBvdXRsaWVycwotIGluY29tcGxldGUgY2FzZXMKCgpgYGB7cn0KaHVtYW5fc3RhcndhcnMgPC0gZmlsdGVyKHN0YXJ3YXJzLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHNwZWNpZXMgPT0gIkh1bWFuIikKaGVhZChodW1hbl9zdGFyd2FycykKYGBgCgoKYGBge3J9CmZpbHRlcihzdGFyd2FycywKICAgICAgIHNwZWNpZXMgPT0gIkh1bWFuIiwgaGVpZ2h0ID4gMTgwKQpgYGAKCiMjIENvbWJpbmluZyBhIGxvdCBvZiBmdW5jdGlvbnMKYGBge3J9CmRmIDwtIG55Y2ZsaWdodHMyMzo6ZmxpZ2h0cwptZWFuKHB1bGwoZmlsdGVyKGZpbHRlcihkZiwgb3JpZ2luID09ICJKRksiKSwgY2FycmllciA9PSAiQUEiLCAhaXMubmEoYXJyX2RlbGF5KSksIGFycl9kZWxheSkpCmBgYAoKVXNpbmcgYSBwaXBlIGAgJT4lIGAuIFNob3J0Y3V0IGlzIGBjdHJsICsgc2hpZnQgKyBNYCBvciBgY29tbWFuZCArIHNoaWZ0ICsgTWAKYGBge3J9CmRmICU+JQogIGZpbHRlcihvcmlnaW4gPT0gIkpGSyIpICU+JQogIGZpbHRlcihjYXJyaWVyID09ICJBQSIpICU+JQogIGZpbHRlcighaXMubmEoYXJyX2RlbGF5KSkgJT4lCiAgcHVsbChhcnJfZGVsYXkpICU+JQogIG1lYW4oKQpgYGAKCkV2ZW4gbmljZXIKYGBge3J9CmRmICU+JQogIGZpbHRlcihvcmlnaW4gPT0gIkpGSyIsCiAgICAgICAgIGNhcnJpZXIgPT0gIkFBIiwKICAgICAgICAgIWlzLm5hKGFycl9kZWxheSkpICU+JQogIHN1bW1hcmlzZShhdmdfZGVsYXkgPSBtZWFuKGFycl9kZWxheSkpICU+JSAKICBwdWxsKGF2Z19kZWxheSkKYGBgCgoqTk9URSogIApUaGUgZnVuY3Rpb24gYHB1bGxgIGlzIGEgZnVuY3Rpb24gdGhhdCB0dXJucyBhIGNvbHVtbiBpbnRvIGEgdmVjdG9yCkZvciBleGFtcGxlIGlmIHlvdSBoYXZlIGEgY29sdW1uIGNhbGxlZCAiZ2VuZXMiLCB1c2luZyBwdWxsIHR1cm5zIHRoZSBjb2x1bW4gaW50byBhIHZlY3RvciB3aXRoIGFsbCB0aGVzZSBnZW5lcwoKCiMjIENvbW1vbiBwYXR0ZXJucwpgYGB7cn0Kc3RhcndhcnMgJT4lIGZpbHRlcihpcy5uYShtYXNzKSkgICAgICAgICAgICAgICAgIyBtaXNzaW5nIHZhbHVlcwpzdGFyd2FycyAlPiUgZmlsdGVyKCFpcy5uYShoZWlnaHQpKSAgICAgICAgICAgICAjIG5vbi1taXNzaW5nCnN0YXJ3YXJzICU+JSBmaWx0ZXIoYmV0d2VlbihoZWlnaHQsIDE1MCwgMjAwKSkgICMgd2l0aGluIGEgcmFuZ2UKYGBgCgoKIyMgMi4gYXJyYW5nZSgpIOKAlCBzb3J0IHJvd3MKCmBgYHtyfQpzdGFyd2FycyAlPiUgCiAgYXJyYW5nZShkZXNjKGhlaWdodCkpCmBgYAoKCgojIyAzLiBkaXN0aW5jdCgpIOKAlCB1bmlxdWUgcm93cwoKYGBge3J9CnN0YXJ3YXJzICU+JSAKICBkaXN0aW5jdChzcGVjaWVzLCBob21ld29ybGQpCmBgYAoKIyMgNC4gc2xpY2VfbWF4LCBzbGljZV9taW4sIHNsaWNlX2hlYWQsIGFuZCBzbGljZV90YWlsCgpgYGB7cn0KREVHcyA8LSByZWFkeGw6OnJlYWRfeGxzeCgiREVHcy54bHN4IikKCkRFR3MgJT4lIAogIG11dGF0ZShwY3RfZGlmZiA9IHBjdC4xIC0gcGN0LjIpICU+JSAKICByZWxvY2F0ZShwY3RfZGlmZiwgLmFmdGVyID0gcGN0LjIpICU+JSAKICBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lIAogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSAKICBzbGljZV9tYXgobiA9IDEwLCBvcmRlcl9ieSA9IGF2Z19sb2cyRkMpCmBgYAoKCmBzbGljZV9tYXhgIGZvciBlYWNoIGdyb3VwLCBnZXQgdGhlICp0b3AqIG4gb2JzZXJ2YXRpb25zCmBzbGljZV9taW5gIGZvciBlYWNoIGdyb3VwLCBnZXQgdGhlICpib3R0b20qIG4gb2JzZXJ2YXRpb25zIApgc2xpY2VfaGVhZGAgIGZvciBlYWNoIGdyb3VwLCBnZXQgdGhlICpmaXJzdCogbiBvYnNlcnZhdGlvbnMKYHNsaWNlX3RhaWxgICBmb3IgZWFjaCBncm91cCwgZ2V0IHRoZSAqbGFzdCogbiBvYnNlcnZhdGlvbnMKCklmIG5vdCBncm91cGVkIAoKYHNsaWNlX21heCgpYCDihpIgZ2V0IHRoZSB0b3AgbiByb3dzIG92ZXJhbGwgYmFzZWQgb24gYSB2YXJpYWJsZS4KYHNsaWNlX21pbigpYCDihpIgZ2V0IHRoZSBib3R0b20gbiByb3dzIG92ZXJhbGwgYmFzZWQgb24gYSB2YXJpYWJsZS4KYHNsaWNlX2hlYWQoKWAg4oaSIGdldCB0aGUgZmlyc3QgbiByb3dzIGluIHRoZSBkYXRhc2V0Lgpgc2xpY2VfdGFpbCgpYCDihpIgZ2V0IHRoZSBsYXN0IG4gcm93cyBpbiB0aGUgZGF0YXNldC4KCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBGdW5jdGlvbnMgb24gY29sdW1ucwoKIyMgMS4gc2VsZWN0KCksIHJlbmFtZSgpLCByZWxvY2F0ZSgpCgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgc2VsZWN0KG5hbWUsIGhlaWdodCwgc3BlY2llcywgbWFzcykgJT4lCiAgcmVuYW1lKCJ3ZWlnaHRfa2ciID0gIm1hc3MiKSAlPiUKICByZWxvY2F0ZSh3ZWlnaHRfa2csIC5hZnRlciA9IGhlaWdodCkKYGBgCgpgcmVuYW1lKGRhdGFmcmFtZSwgbmV3Y29sbmFtZSA9IG9sZG5hbWUpYCByZW5hbWVzIGFuIGV4aXN0aW5nIGNvbHVtbiBuYW1lIHRvIGFub3RoZXIKU29tZSBSIHZlcnNpb25zIHJlcXVpcmVzICIiIGZvciBjb2x1bW4gcmVuYW1pbmcKCgpgcmVsb2NhdGVgIGZ1bmN0aW9uIGFsbG93cyB0aGUgbmV3IGNvbHVtbiBpcyBhZGRlZCBpbiBhIHNwZWNpZmljIHBvc2l0aW9uLCBkZWZhdWx0IChsYXN0IGNvbHVtbikKCgojIyAyLiBSZW1vdmluZyBhIGNvbHVtbgoKVGhlIGZ1bmN0aW9uIGBzZWxlY3RgIGNhbiBiZSB1c2VkIHRvIHBvc2l0aXZlbHkgb3IgbmVnYXRpdmVseSBzZWxlY3QgY29sdW1ucyAoaW4gYW55IG9yZGVyKQoKCmBgYHtyfQpkZiAlPiUgCiAgc2VsZWN0KGRheSwgbW9udGgsIGRlcF90aW1lKQpgYGAKCgpXZSBjYW4gdXNlIGBzZWxlY3QoZGYsIC1jb2wxKWAgZm9yIG9uZSBjb2x1bW4gb3IgbXVsdGlwbGUgY29sdW1ucyBsaWtlIGBzZWxlY3QoZGYsIC1jb2wxLCAtY29sMiwgLWNvbDMpYCBvciBldmVuIGJldHRlciB1c2UgKGFueV9vZikKCmBgYHtyfQpjb2xzX3RvX2Ryb3AgPC0gYygieWVhciIsICJjYXJyaWVyIikKCmRmICU+JSAKICBzZWxlY3QoLWFueV9vZihjb2xzX3RvX2Ryb3ApKQpgYGAKCgpgYGB7cn0KZGYgJT4lIAogIHNlbGVjdChzdGFydHNfd2l0aCgiZGVwIikpCgpkZiAlPiUgCiAgc2VsZWN0KGVuZHNfd2l0aCgidGltZSIpKQoKZGYgJT4lIAogIHNlbGVjdChjb250YWlucygiZGVsIikpCgpkZiAlPiUgCiAgc2VsZWN0KG1hdGNoZXMoIihkZWxheXx0aW1lKSQiKSkKCmBgYAoKYGBge3J9CmRmICU+JSAKICBzZWxlY3Qod2hlcmUoaXMubnVtZXJpYykpCgpkZiAlPiUgCiAgc2VsZWN0KHdoZXJlKGlzLmNoYXJhY3RlcikpCmBgYAoKCiMjIDMuIFR1cm5pbmcgYSBjb2x1bW4gaW50byBtdWx0aXBsZQpgYGB7cn0KZGZfdGltZV9ob3VyIDwtIGRmICU+JSBzZWxlY3QodGltZV9ob3VyKQpoZWFkKGRmX3RpbWVfaG91cikKYGBgCgoKVGhlIGZ1bmN0aW9uIGBzZXBhcmF0ZShkYXRhZnJhbWUsIGNvbCA9ICJjb2x1bW5fdG9fc3BsaXQiLCBpbnRvID0gYygicGllY2UxIiwgInBpZWNlMiIpLCBzZXAgPSAic2VwYXJhdG9yIikpYAoKYGBge3J9CmRmX3RpbWVfaG91ciA8LSAgZGZfdGltZV9ob3VyICU+JSBzZXBhcmF0ZShzZXAgPSAiICIsIAogICAgICAgICAgICAgICAgY29sID0gdGltZV9ob3VyLCAKICAgICAgICAgICAgICAgIGludG8gPSBjKCJ5ZWFybW9udGhkYXkiLCAidGltZSIpLCAKICAgICAgICAgICAgICAgIHJlbW92ZSA9IFQpCmhlYWQoZGZfdGltZV9ob3VyKQpgYGAKCmBgYHtyfQpkZl90aW1lX2hvdXIgPC0gZGZfdGltZV9ob3VyICU+JSAKICBzZXBhcmF0ZShzZXAgPSAiLSIsIAogICAgICAgICAgICAgICAgY29sID0geWVhcm1vbnRoZGF5LCAKICAgICAgICAgICAgICAgIGludG8gPSBjKE5BLCAibW9udGgiLCAiZGF5IiksIAogICAgICAgICAgICAgICAgcmVtb3ZlID0gVCkKaGVhZChkZl90aW1lX2hvdXIpCmBgYApUaGUgc2FtZSBmdW5jdGlvbiBjYW4gYmUgYXBwbGllZCBvbiB0aGUgdGltZSBjb2x1bW4sIGludG8gaG91ciwgbWludXRlLCBzZWNvbmRzCgpgYGB7cn0KZGZfdGltZV9ob3VyIDwtIGRmX3RpbWVfaG91ciAlPiUgCiAgc2VwYXJhdGUoc2VwID0gIjoiLCAKICAgICAgICAgICAgICAgIGNvbCA9IHRpbWUsIAogICAgICAgICAgICAgICAgaW50byA9IGMoImhyIiwgIm1uIiwgInNlYyIpLCAKICAgICAgICAgICAgICAgIHJlbW92ZSA9IFQpCmhlYWQoZGZfdGltZV9ob3VyKQpgYGAKIyMgNC4gdW5pdGluZyBjb2x1bW5zIHdpdGggdW5pdGUoKQoKYGBge3J9CmRmX3RpbWVfaG91ciAlPiUKICB1bml0ZSgibmV3Y29sIiwgbW9udGgsIGRheSwgaHIsIG1uLCBzZWMsIHNlcCA9ICItIiwgcmVtb3ZlID0gRkFMU0UpCmBgYAoKIyMgNS4gbXV0YXRlKCkg4oCUIGNyZWF0ZSBuZXcgdmFyaWFibGVzCgpBZGRpbmcgYSBjb2x1bW4gZm9sbG93cyB0aGlzIHN0cnVjdHVyZSBgbXV0YXRlKGRmLCAibmV3Y29sdW1uIiA9IGRmJGV4aXN0aW5nY29sdW1uKWAgb3IgIGBtdXRhdGUoZGYsIG5ld2NvbHVtbiA9IGRmJGV4aXN0aW5nY29sdW1uKWAKCmBgYHtyfQpzdGFyd2FycyAlPiUKICBtdXRhdGUoaGVpZ2h0X20gPSBoZWlnaHQgLyAxMDAsIAogICAgICAgICBibWkgPSBtYXNzIC8gKGhlaWdodF9tXjIpKSAlPiUKICBzZWxlY3QobmFtZSwgaGVpZ2h0LCBtYXNzLCBoZWlnaHRfbSwgYm1pKQpgYGAKCmBgYHtyfQpuZXdfZGYgPC0gbnljZmxpZ2h0czIzOjpmbGlnaHRzICU+JSAKICBtdXRhdGUoImRhZyIgPSBkYXkpCgpuZXdfZGYgPC0gbmV3X2RmICU+JSAKICBtdXRhdGUoIkRheV9Nb250aF9ZZWFyIiA9IHN0cl9jKG5ld19kZiRkYXksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3X2RmJG1vbnRoLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19kZiR5ZWFyLCBzZXAgPSAiXyIpKSAlPiUKICByZWxvY2F0ZShEYXlfTW9udGhfWWVhciwgLmJlZm9yZSA9IHllYXIpCmhlYWQobmV3X2RmKQpgYGAKCiMjIyBjYXNlX3doZW4oKSDigJQgY2F0ZWdvcmljYWwgdmFyaWFibGVzCgpgbXV0YXRlYCB3aXRoIGBjYXNlX3doZW5gIGNhbiBiZSB1c2VkIHRvIGFkZCBhIGNvbHVtbiBiYXNlZCBvbiBhIGNvbmRpdGlvbiBvZiBhbm90aGVyIGNvbHVtbiB0aGF0IGZvbGxvd3MgYSBwYXR0ZXJuLgoKbXV0YXRlKE5ld0NvbCA9IGNhc2Vfd2hlbihncmVwbCgicGF0dGVybiIsIEV4aXN0aW5nQ29sdW1uKSB+IAoiQWRkZWRUZXh0IiwgClRSVUUgfiAiQWRkZWRUZXh0MiIpKSAgCgp3aGljaCBtZWFucyBBc3NpZ24gIkFkZGVkVGV4dCIgaWYgdGhlIHBhdHRlcm4gZXhpc3RzIGluIHRoZSBFeGlzdGluZ0NvbHVtbiwgaWYgbm90LCBhc3NpZ24gIkFkZGVkVGV4dDIiCgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgbXV0YXRlKGhlaWdodF9ncm91cCA9IGNhc2Vfd2hlbigKICAgIGhlaWdodCA8IDE1MCB+ICJzaG9ydCIsCiAgICBoZWlnaHQgPCAxOTAgfiAibWVkaXVtIiwKICAgIFRSVUUgfiAidGFsbCIpCiAgICApICU+JQogIGNvdW50KGhlaWdodF9ncm91cCkKYGBgCgojIyA2LjEgR3JvdXBpbmcKCmBncm91cF9ieWAgaXMgdXNlZCB0byBncm91cCB0aGUgZGF0YWZyYW1lIGJ5IGEgY2VydGFpbiBjb2x1bW4sIHdoZXJlIGFub3RoZXIgZnVuY3Rpb24gY291bGQgYmUgYXBwbGllZCBvbiBlYWNoIGdyb3VwLiBGb3IgaW5zdGFuY2UsIGhlcmUgd2UgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIGFycml2YWwgZGVsYXkgZm9yIGVhY2ggY2FycmllcgoKYGBge3J9CmRmICU+JQogIGdyb3VwX2J5KGNhcnJpZXIpICU+JQogIHN1bW1hcmlzZSgKICAgIGF2Z19hcnJfZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSwKICAgIG5fZmxpZ2h0cyA9IG4oKQogICkKYGBgCgoKIyMgNi4yIGdyb3VwX2J5KCkgKyBzdW1tYXJpc2UoKQoKYGBge3J9CnN0YXJ3YXJzICU+JQogIGdyb3VwX2J5KHNwZWNpZXMpICU+JQogIHN1bW1hcmlzZSgKICAgIG4gPSBuKCksCiAgICBtZWFuX2hlaWdodCA9IG1lYW4oaGVpZ2h0LCBuYS5ybSA9IFRSVUUpCiAgKSAlPiUKICBhcnJhbmdlKGRlc2MobikpCmBgYAoKIyMgNy4gYWNyb3NzKCkg4oCUIG11bHRpLWNvbHVtbiBvcGVyYXRpb25zCgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgc3VtbWFyaXNlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfiBtZWFuKC54LCBuYS5ybSA9IFRSVUUpKSkKYGBgCgoKIyBNZXJnaW5nL0pvaW5pbmcgZGF0YWZyYW1lcwoKYGBge3J9CnNldC5zZWVkKDEyMzQ1KQpwYXRpZW50X3RhYmxlIDwtIHRpYmJsZSgKICBwYXRpZW50X2lkID0gcGFzdGUwKCJQIiwgc3RyX3BhZCgxOjEyLCAzLCBwYWQgPSAiMCIpKSwKICBkaWFnbm9zaXMgPSBzYW1wbGUoYygiY29udHJvbCIsICJzZXBzaXMiLCAiY2FuY2VyIiksIDEyLCByZXBsYWNlID0gVFJVRSksCiAgYWdlID0gc2FtcGxlKDMwOjkwLCAxMiwgcmVwbGFjZSA9IFRSVUUpLAogIHNleCA9IHNhbXBsZShjKCJGIiwgIk0iKSwgMTIsIHJlcGxhY2UgPSBUUlVFKQopCgpzaXRlX3RhYmxlIDwtIHRpYmJsZSgKICBwYXRpZW50X2lkID0gcGFzdGUwKCJQIiwgc3RyX3BhZChzYW1wbGUoMToxMiwgMTApLCAzLCBwYWQgPSAiMCIpKSwKICBob3NwaXRhbCA9IHNhbXBsZShjKCJTaXRlQSIsICJTaXRlQiIsICJTaXRlQyIpLCAxMCwgcmVwbGFjZSA9IFRSVUUpLAogIGNvdW50cnkgPSBzYW1wbGUoYygiVVMiLCAiREUiLCAiRlIiKSwgMTAsIHJlcGxhY2UgPSBUUlVFKQopCgpwYXRpZW50X3RhYmxlCnNpdGVfdGFibGUKCmBgYAoKIyMgbGVmdF9qb2luKCkKCmBgYHtyfQpwYXRpZW50c19mdWxsIDwtIHBhdGllbnRfdGFibGUgJT4lCiAgbGVmdF9qb2luKHNpdGVfdGFibGUsIGJ5ID0gInBhdGllbnRfaWQiKQoKcGF0aWVudHNfZnVsbApgYGAKCiMjIERlYnVnOiB3aG8gZGlkIE5PVCBtYXRjaD8KCmBgYHtyfQpwYXRpZW50X3RhYmxlICU+JQogIGFudGlfam9pbihzaXRlX3RhYmxlLCBieSA9ICJwYXRpZW50X2lkIikKYGBgCgpgYGB7cn0KYWlybGluZXNfZGYgPC0gbnljZmxpZ2h0czIzOjphaXJsaW5lcwpoZWFkKGFpcmxpbmVzX2RmKQpgYGAKCgpgYGB7cn0KZmxpZ2h0c19kZiA8LSBueWNmbGlnaHRzMjM6OmZsaWdodHMKbWVyZ2VkX2RmIDwtIGxlZnRfam9pbihmbGlnaHRzX2RmLCBhaXJsaW5lc19kZiwgYnkgPSAiY2FycmllciIpIAoKbWVyZ2VkX2RmICU+JSAgCiAgZHBseXI6OnJlbmFtZShhaXJsaW5lc19ubSA9IG5hbWUpICU+JSAKICByZWxvY2F0ZShhaXJsaW5lc19ubSwgLmFmdGVyID0gY2FycmllcikKYGBgCgpgbGVmdF9qb2luKHgsIHkpYAlBbGwgZnJvbSB4ICAKYHJpZ2h0X2pvaW4oeCwgeSlgCUFsbCBmcm9tIHkgIApgaW5uZXJfam9pbih4LCB5KWAJT25seSBtYXRjaGluZyByb3dzICAKYGZ1bGxfam9pbih4LCB5KWAJQWxsIHJvd3MgZnJvbSBib3RoIHRhYmxlcywgZmlsbGluZyB0aGUgbWlzc2luZyB3aXRoIE5BcyAgCgoKCgojIFJlc2hhcGluZyBkYXRhIChXaWRlIHZzIExvbmcgZGF0YSBzaGFwZSkKYGBge3J9CnNldC5zZWVkKDEyMzQ1KQoKbWV0YWRhdGEgPC0gdGliYmxlKAogIHNhbXBsZV9pZCA9IHBhc3RlMCgiUyIsIHN0cl9wYWQoMToyNCwgMywgcGFkID0gIjAiKSksCiAgcGF0aWVudF9pZCA9IHBhc3RlMCgiUCIsIHN0cl9wYWQoc2FtcGxlKDE6MTIsIDI0LCByZXBsYWNlID0gVFJVRSksIDMsIHBhZCA9ICIwIikpLAogIHRpc3N1ZSA9IHNhbXBsZShjKCJ0dW1vciIsICJub3JtYWwiKSwgMjQsIHJlcGxhY2UgPSBUUlVFKSwKICB0cmVhdG1lbnQgPSBzYW1wbGUoYygiZHJ1Z0EiLCAiZHJ1Z0IiLCAicGxhY2VibyIpLCAyNCwgcmVwbGFjZSA9IFRSVUUpLAogIHNleCA9IHNhbXBsZShjKCJGIiwgIk0iKSwgMjQsIHJlcGxhY2UgPSBUUlVFKSwKICBhZ2UgPSBzYW1wbGUoMzA6ODUsIDI0LCByZXBsYWNlID0gVFJVRSksCiAgYmF0Y2ggPSBzYW1wbGUoYygiYmF0Y2gxIiwgImJhdGNoMiIpLCAyNCwgcmVwbGFjZSA9IFRSVUUpCikKCm1ldGFkYXRhCgpnZW5lcyA8LSBwYXN0ZTAoIkdlbmUiLCAKICAgICAgICAgICAgICAgIHN0cl9wYWQoMTo1MCwgNCwgCiAgICAgICAgICAgICAgICAgICAgICAgIHBhZCA9ICIwIikpCgpjb3VudHMgPC0gbWF0cml4KAogIHJuYmlub20oNTAgKiAyNCwgbXUgPSA4MCwgc2l6ZSA9IDEpLAogIG5yb3cgPSA1MCwKICBuY29sID0gMjQsCiAgZGltbmFtZXMgPSBsaXN0KGdlbmVzLCBtZXRhZGF0YSRzYW1wbGVfaWQpCikKCmNvdW50c19kZiA8LSBhc190aWJibGUoY291bnRzLCByb3duYW1lcyA9ICJnZW5lX2lkIikKY291bnRzX2RmCmBgYAoKUHJvYmxlbTogVGhpcyBpcyAid2lkZSIuIE1hbnkgYW5hbHlzZXMgbmVlZCAibG9uZyIuCgoKIyMgMS4gcGl2b3RfbG9uZ2VyKCk6IHdpZGUgLT4gbG9uZwoKYGBge3J9CmNvdW50c19sb25nIDwtIGNvdW50c19kZiAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gc3RhcnRzX3dpdGgoIlMiKSwKICAgIG5hbWVzX3RvID0gInNhbXBsZV9pZCIsCiAgICB2YWx1ZXNfdG8gPSAiY291bnQiCiAgKQoKY291bnRzX2xvbmcKYGBgCgpOb3cgZWFjaCByb3cgaXM6IGdlbmUgw5cgc2FtcGxlLgoKCk5vdyB3ZSBqb2luIGNvdW50cyB3aXRoIG1ldGFkYXRhCgpgYGB7cn0KaGVhZChtZXRhZGF0YSkKYGBgCgoKYGBge3J9CmNvdW50c19hbm5vdCA8LSBjb3VudHNfbG9uZyAlPiUKICBsZWZ0X2pvaW4obWV0YWRhdGEsIGJ5ID0gInNhbXBsZV9pZCIpCgpjb3VudHNfYW5ub3QgJT4lIGdsaW1wc2UoKQpgYGAKCgoKSGVyZSB3ZSBzdW1tYXJpc2UgZXhwcmVzc2lvbiBieSBncm91cAoKRXhhbXBsZTogbWVhbiBjb3VudHMgYnkgdGlzc3VlIHBlciBnZW5lCgpgYGB7cn0KZ2VuZV9zdW1tYXJ5IDwtIGNvdW50c19hbm5vdCAlPiUKICBncm91cF9ieShnZW5lX2lkLCB0aXNzdWUpICU+JQogIHN1bW1hcmlzZShtZWFuX2NvdW50ID0gbWVhbihjb3VudCksIC5ncm91cHMgPSAiZHJvcCIpCgpnZW5lX3N1bW1hcnkKYGBgCgojIyAyLiBwaXZvdF93aWRlcigpOiBsb25nIC0+IHdpZGUKCkNyZWF0ZSBhIGdlbmUgw5cgdGlzc3VlIHRhYmxlICgyIGNvbHVtbnM6IHR1bW9yL25vcm1hbCkKCmBgYHtyfQpnZW5lX3dpZGUgPC0gZ2VuZV9zdW1tYXJ5ICU+JQogIHBpdm90X3dpZGVyKAogICAgbmFtZXNfZnJvbSA9IHRpc3N1ZSwKICAgIHZhbHVlc19mcm9tID0gbWVhbl9jb3VudAogICkKCmdlbmVfd2lkZQpgYGAKCgojIyAzLiBCaW5kaW5nIHJvd3MgYW5kIGNvbHVtbnMKCmBgYHtyfQp2aXNpdF9hIDwtIHRpYmJsZSgKICBpZCA9IGMoIlAwMSIsIlAwMiIsIlAwMyIpLAogIHZpc2l0ID0gImJhc2VsaW5lIiwKICBjcnAgPSBjKDIuMSwgNS40LCAxLjkpCikKCnZpc2l0X2IgPC0gdGliYmxlKAogIGlkID0gYygiUDA0IiwiUDA1IiksCiAgdmlzaXQgPSAid2VlazQiLAogIGNycCA9IGMoMy4zLCAyLjgpLAogIHdiYyA9IGMoNi4xLCA1LjcpICAgIyBleHRyYSBjb2x1bW4gbm90IGluIHZpc2l0X2EKKQoKYGBgCgoKIyMjIDMuMSByYmluZCB2cyBiaW5kX3Jvd3MgKHN0YWNraW5nIHJvd3MpCmBgYHtyfQpyYmluZCh2aXNpdF9hLCB2aXNpdF9iKQpgYGAKCgpgYGB7cn0KYmluZF9yb3dzKHZpc2l0X2EsIHZpc2l0X2IpCmBgYApoZXJlIGBiaW5kX3Jvd3NgIGRvZXMgbm90IGdpdmUgYW4gZXJyb3IsIG1ha2luZyBzdXJlIHJvd3MgYXJlIHN0YWNrZWQsIGFuZCBtaXNzaW5nIGNvbHVtbnMgYXJlIGNyZWF0ZWQgYW5kIGZpbGxlZCB3aXRoIE5BICh3YmMgaXMgTkEgZm9yIGJhc2VsaW5lIHJvd3MpCgoKIyMjIDMuMiBiaW5kX2NvbHMoKSB2cyBjYmluZCgpIChnbHVpbmcgY29sdW1ucykKCmBgYHtyfQpwYXRpZW50cyA8LSB0aWJibGUoCiAgaWQgPSBjKCJQMDEiLCJQMDIiLCJQMDMiKSwKICBzZXggPSBjKCJGIiwiTSIsIkYiKQopCgpsYWJzIDwtIHRpYmJsZSgKICBjcnAgPSBjKDIuMSwgNS40LCAxLjkpLAogIHdiYyA9IGMoNS44LCA2LjIsIDUuMSkKKQoKYGBgCgpgYGB7cn0KY2JpbmQocGF0aWVudHMsIGxhYnMpCmBgYAoKYGBge3J9CmJpbmRfY29scyhwYXRpZW50cywgbGFicykKYGBgCgpgYGB7cn0KbGFic19zaG9ydCA8LSB0aWJibGUoY3JwID0gYygyLjEsIDUuNCkpCgpjYmluZChwYXRpZW50cywgbGFic19zaG9ydCkKYGBgCgpgYGB7cn0KYmluZF9jb2xzKHBhdGllbnRzLCBsYWJzX3Nob3J0KQpgYGAKCiMgUmVjYXAgCgojIyAxLiAqKkNvbHVtbi13aXNlKiogZnVuY3Rpb25zIAojIyMgKG1vZGlmeSBvciBjaG9vc2UgY29sdW1ucyk6IAoKLSBzZWxlY3QoKSAgCgotIHJlbmFtZSgpICAKCi0gcmVsb2NhdGUoKSAgCgojIyMgQ3JlYXRlIG9yIG1vZGlmeSBjb2x1bW5zICAKCi0gbXV0YXRlKCkgIAoKIyMjIElkZW50aWZ5IGNvbHVtbnMgdXNpbmcgdGlkeXNlbGVjdCBoZWxwZXJzOiAgCi0gc3RhcnRzX3dpdGgoKSwgZW5kc193aXRoKCksIGNvbnRhaW5zKCksIG1hdGNoZXMoKSwgd2hlcmUoKQoKCiMjIDIuICoqUm93LXdpc2UqKiBmdW5jdGlvbnMgKGZpbHRlciwgc2xpY2UsIHJvdyBvcGVyYXRpb25zKQojIyMgS2VlcCBvciByZW1vdmUgcm93cwoKLSBmaWx0ZXIoKQoKLSBzbGljZSgpLCBzbGljZV9oZWFkKCksIHNsaWNlX3RhaWwoKSwgc2xpY2VfbWluKCksIHNsaWNlX21heCgpCgojIyMgT3JkZXIgcm93cwoKLSBhcnJhbmdlKCkKCgojIyAzLiAqKkdyb3VwLXdpc2UqKiBmdW5jdGlvbnMgCiMjIyAob3BlcmF0ZSB3aXRoaW4gZ3JvdXBzKQoKIyMjIEdyb3VwaW5nIGNoYW5nZXMgaG93IG90aGVyIGZ1bmN0aW9ucyBiZWhhdmU6Cgpncm91cF9ieSgpLCB1bmdyb3VwKCkKCiMjIDQuICoqRGF0YS1mcmFtZeKAk3dpc2UqKiBmdW5jdGlvbnMgCiMjIyAob3BlcmF0ZSBvbiB3aG9sZSB0YWJsZSkKCi0gYmluZF9yb3dzKCkKCi0gYmluZF9jb2xzKCkKCi0gbGVmdF9qb2luKCksIGlubmVyX2pvaW4oKSwgZnVsbF9qb2luKCksIGFudGlfam9pbigpCgoKIyMjIFJlc2hhcGUgZGF0YToKCnBpdm90X2xvbmdlcigpIGFuZCBwaXZvdF93aWRlcigpCgoKIyMgNS4gKipTdW1tYXJ5KiogZnVuY3Rpb25zIAojIyMgKHVzdWFsbHkgaW5zaWRlIHN1bW1hcmlzZSgpKQoKbWVhbigpLCBtZWRpYW4oKSwgc3VtKCksIG4oKSwgbl9kaXN0aW5jdCgpLCBzZCgpLCB2YXIoKQoKCgojIEdvb2QtdG8ta25vdyBzeW1ib2xzIG5hbWVzCigpICBwYXJlbnRoZXNlcyAgCltdICBzcXVhcmUgYnJhY2tldHMgIAp7fSAgY3VybHkgYnJhY2VzICAKKiAgYXN0ZXJpc2sgIAomICBhbXBlcnNhbmQgIApeICBoYXQgIAovICBmb3J3YXJkIHNsYXNoICAKXFwgIGJhY2tzbGFzaCAgCicnICBzaW5nbGUgcXVvdGVzICAKIiIgIGRvdWJsZSBxdW90ZXMgIAotICBoeXBoZW4gKGRhc2gpICAKXyAgdW5kZXJzY29yZSAgCn4gIHRpbGRhICAKYCAgYmFja3RpY2sgIAohICBleGNsYW1hdGlvbiBtYXJrICAKLiAgcGVyaW9kICAKLCAgY29tbWEgIAo6ICBjb2xvbiAgCjsgIHNlbWktY29sb24gIAokICBkb2xsYXIgc2lnbiAgCiMgIHNoYXJwICAKJSAgcGVyY2VudGFnZSAgCiAgCg==