Workshop goals
By the end of this workshop you should be able to:
- understand tidy data and why it matters for data
analysis
- clean and wrangle data using dplyr (filter, select,
mutate, summarise)
- reshape data using tidyr
(
pivot_longer(), pivot_wider(),
separate())
- combine datasets using joins
(
right_join(), left_join(),
inner_join(), anti_join())
- handle real messy cases: missing values, wrong column types, messy
IDs, duplicate keys
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
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…
- state >> The two-letter code for the state (or
territory, etc) where the hospital is located.
- condition >> The condition for which the patient was
admitted. Six categories of conditions are included in the data.
- measure_id >> The ID of the thing being measured.
Note that there are 22 unique IDs but only 21 unique names.
- measure_name >> The name of the thing being measured.
Note that there are 22 unique IDs but only 21 unique names.
- score >> The score of the measure.
- footnote >> Footnotes that apply to this measure: – 5
= “Results are not available for this reporting period.”, – 25 = “State
and national averages include Veterans Health Administration (VHA)
hospital data.”, – 26 = “State and national averages include Department
of Defense (DoD) hospital data.”.
- start_date >> date The date on which measurement
began for this measure.
- end_date >> date The date on which measurement ended
for this measure.
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()
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()
3. Group-wise functions
(operate within groups)
Grouping changes how other functions behave:
group_by(), ungroup()
4. Data-frame–wise functions
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==