Setup

Install

install.package("package_name")

Load

library(package_name)

Import

From csv

library(tidyverse)
data <- read_csv("folder/filename.csv")

From a googlesheet

library(googlesheets)
sheet <- gs_title(“Google sheet name”)
data <- sheet %>% gs_read(ws = “Worksheet name”)

From a table on a webpage

library(rvest)
url <- read_html('url')
data_raw <- url %>%
              html_node("table") %>%
              html_table(fill=TRUE)

From a PDF

library(SPARQL) # SPARQL querying package
library(ggplot2)

# Step 1 - Set up preliminaries and define query
# Define the data.gov endpoint
endpoint <- "http://services.data.gov/sparql"

# create query statement
query <-
"PREFIX  dgp1187: <http://data-gov.tw.rpi.edu/vocab/p/1187/>
SELECT ?ye ?fi ?ac
WHERE {
?s dgp1187:year ?ye .
?s dgp1187:fires ?fi .
?s dgp1187:acres ?ac .
}"

# Step 2 - Use SPARQL package to submit query and save results to a data frame
qd <- SPARQL(endpoint,query)
df <- qd$results

From multiple csvs

  1. Create a vector of all the datasets
  all_datasets <- tibble("file" = c('dataset_name_1', 'dataset_name_2')) 
  1. Create a function to import one dataset
  import <- function(df){
    df <- read_csv(str_c("df, ".csv"))
  }
  1. Use purrr::map() to run the function on (each element) of all the datasets
  all_data <- all_datasets %>% mutate(dataset = purrr::map(file, import))
  1. Run a ‘for loop’ to get R to assign each tibble to an object
  for(i in 1:nrow(all_data)){
  assign(all_data$file[[i]], all_data$dataset[[i]])
  }

From multiple webpages

  1. Create a vector with the URL paths needed, eg:
  url_unique <- # function to list these, eg using seq() if numerical
  url_base <- "https://abc"
  url_end <- "/abc.html"
  url_path <- url_unique %>% str_c(url_base, . , url_end)
  data <- tibble(url_path)
  1. Define a function to scrape (and clean) data from one url
  scrape <- function(url){
  xml <- read_html(url) %>%
          html_node("table") %>%
          html_table(fill=TRUE) %>%
  xml
  1. (if needed) Prevent error messages from URL paths that don’t exist
  extract_results <- function(df){
    df$result
  }
  1. Loop scrape function over all URLs as nested tibbles
  data <- data %>%
            mutate(new_column = purrr::map(url_path, safely(scrape)),
            new_column = map(new_column, extract_results))

Save as csv

write_csv(dataset, "filename.csv")

Tidy

Inspect

Get list of all column headings in a dataset

colnames(dataset)

Get the number of rows and columns in a dataset

summarise() 

Get the breakdown of results within a column

table(dataset$column)

Get the number of values in a column

summary(dataset$column)

Get the number of NA values in a column

sum(is.na(dataset$column))

tidyr

Wide –> skinny

gather(dataset, "new_column_name1", "new_column_name2", column range eg '2:8')

Skinny –> wide

spread(dataset, $keycolumn, $valuecolumn)

Combine datasets by a common column

merge(dataset1, dataset2, by = "common column name")

Combine datasets by attaching rows

append(dataset1, dataset2)

Create a new column

mutate(old_variable, new_variable = (calculation))

Rename a column

rename(dataset, new name = old name)

Rename categories within a column

data %>% 
  mutate(variable = fct_recode(variable, "new name" = "old name", 
                                         "new name = "old name"))

Alternatives (more for correcting typos like Find & Replace): dataset$column[dataset$column == "old"] <- "new"

"new_name" <- length(grep("existing_label", dataset$column))

Drop column(s) / row(s)

subset(dataset, select = -c(columns))
subset(dataset, column != "")

Remove NAs

na.omit(dataset)

Arrange a column in descending order

arrange(dataset, desc(column)))

stringr

Filter a column if contains text string

grepl(“text string”, variable)

Remove symbols, eg % sign

dataset$column <- gsub("\\%","", dataset$column)

Convert character column to numeric

dataset$column <- as.numeric(as.character(dataset$column))

Round numbers

mutate(variable = round(variable))

janitor

Clean column heading names (to lowercase with underscores

clean_names()

Create a quick pivot table to summarise variables

tabyl(variable1, variable2) %>% 
adorn_*

Remove empty rows and columns

remove_empty() 

Fix dates stored as serial numbers

excel_numeric_to_date()

Transform

dplyr

Select columns

select(variable, variable, variable)

Select rows that meet criteria

filter(variable == "..." | variable == "...")

Analyse (like a pivot table)

group_by(column) %>% 
  summarise(calculation)

Classic code structures

  • Pivot and perform calculations on data
data_analysis <- raw_data %>%
                       filter(),
                       group_by(),
                       summarise()

…which sounds like: “The original dataframe is XXX, now filter data to only include rows that satisfy the conditions YYY, now group the data at each level of the variable(s) ZZZ, now summarize the data and calculate summary functions XXX…”

  • Pivot data to show percentage breakdown of each column
data_analysis <- raw_data %>%
                       group_ by(column 1, column 2) %>%
                       summarise(n = n()) %>%
                       mutate(pct = n / sum(n)) %>%
                       spread(column 2, pct)

Calculations

sum()
count()
unique()
n_distinct()

Count unique values in a column and arrange in descending order with

data %>% count(column) %>% arrange(desc(n))

apply

Pivot one column of data (use 2 for rows) with

sapply(dataset, function, 1)

Get the number of unique values in every column

rapply(data,function(x)length(unique(x)))

stringr

JOIN with str_c()

  • sep = how you combine the columns, eg sep = “,” for comma separated values

  • collapse = how you combine the rows, eg collapse = “-”, to add a hyphen between each vector

SPLIT with str_split()

GET MATCHES

  • str_view() to see results in Viewer window

  • str_detect() to get TRUE/FALSE response

  • str_count() to get counted response

GET MATCHES WHEN WORKING WITH A TABLE with tidyr::extract(old_column, "new_column", "regular expression")

GET LOCATION with str_locate()

SUBSET with str_sub()

COUNT LENGTH with str_length()

WRAP with str_wrap

  • width = line width in characters

  • indent = indentation of first line in each paragraph

  • exdent = indentation of following lines in each paragraph

regex

aka “regular expressions”

- `.` = any character (wildcard)

- `^` = start of the string

- `$` = end of the string

- `match = TRUE` = view just those matches that meet criteria

- `\d:` any digit

- `\s:` any whitespace (space, tab, newline)

- `[abc]:` matches a, b, or c

- `[^abc]:` matches anything except a, b, or c

Visualise 🤩

Basics

Classic code structure

ggplot(dataset) +
     viz_function(aes(x = variable,
                            y = variable,
                            group = variable,
                    size = variable,
                    fill = variable) +
   scale_x/y_discrete/continuous(breaks = c(), 
                      limits = c(), 
                      labels = c()) +
   labs(title = "...",
        subtitle = "...",
        x = "...",
        y = "...",
        caption = "...") +
   theme() +
   ggsave("name.filetype", height = , width = , unit = "")

Additional layers

geom_hline() 
geom_vline()
geom_segment()
geom_text()
geom_rect()

Transform data within a ggplot function

plot1 <- ggplot(filter(dataset, variable == criteria))

Legends

Reorder

  1. First create a factor:
dataset$newcolumn <- factor(dataset$column,  levels = c(“item1”, “item2”) , ordered = TRUE)
  1. Then in ggplot() aesthetic mappings, use this new column, rather than the pre-existing one.

Remove

theme(legend.position = "none")

Axes

Arrange in descending order

ggplot(aes(x = reorder(variable1, -variable2), y = variable2)

Text

Remove / style axes labels

axis.title.x/y = element_blank() or element_text(size = , family = , font = )
axis.text.x/y = element_blank() or element_text(size = , family = , font = )

Wrap long labels

scale_x_discrete(labels = function(x) str_wrap(x, width = 10))

Rotate labels 45 degrees

theme(axis.text.x=element_text(angle=45))

Themes

From ggplot

  • theme_gray() – signature ggplot2 theme

  • theme_bw() – dark on light ggplot2 theme

  • theme_linedraw() – uses black lines on white backgrounds only

  • theme_light() – similar to linedraw() but with grey lines aswell

  • theme_dark() – lines on a dark background instead of light

  • theme_minimal() – no background annotations, minimal feel.

  • theme_classic() – theme with no grid lines

  • theme_void() – empty theme with no elements

From ggthemes

  • theme_solid() - Theme with nothing other than a background color

  • theme_map() - Clean theme for maps

  • theme_igray() - Inverse gray theme

  • theme_economist()

  • theme_fivethirtyeight()

  • theme_wsj()

  • theme_few()

  • theme_tufte()

  • theme_excel()

  • theme_gdocs()

  • theme_stata()

  • theme_solarized()

  • theme_hc() - Highcharts JS theme

Interactive ggplots

  1. iraph
library(iraph)
ggplot(data, aes(x = , y = , tooltip = variable) +
    geom_???_interactive()
  1. plotly
library(plotly)
ggplotly(plot, tooltip = variable)
  1. highcharter

  2. htmlwidgets

  3. RShiny

  • To make a shiny app, you need to create script with a filename ending “app.R”

  • The basic structure of an R Shiny app:

library(shiny)
ui <- fluidPage()
input() functions, eg sliderInput()
output() functions, eg plotOutput()

server <- function(input, output) { }
output$xxxx <- render*(), eg renderPlot()

shinyApp(ui = ui, server = server)

Geographical maps

Introduction

There seems to be lots of different ways you can make maps in R: partly because the best approach will depend on what type of map you’re making; and partly just because there’s lots of competing packages available.

  • Ways of getting map data

    1. Vector image maps, eg sf package or maps package and map_data() function

    2. Raster image maps, eg raster package

  • Packages for plotting map data

    1. ggplot, eg geom_polygon() and geom_sf()

    2. tmap and tmaptools (dunno)

    3. leaflet (dunno)

A note on shapefiles

  • Raster

    • static image files generated previously by the mapping service, which limits your ability to redraw or change the appearance of the geographic map

    • tradeoff means you can immediately focus on incorporating additional data into the map; better for gridded data, like satellite imagery

  • Vector

    • spatial data files which contain detailed information necessary to draw all the components of a map (e.g. points, lines, polygons)

    • requires importing this data into R; once imported though, ggplot2 works with simple features (sf) data frames to easily generate geospatial visualizations using all the core elements and approaches of ggplot()

  • Where to get shapefiles: http://www.naturalearthdata.com/downloads/

Examples

  1. Create a basic map of the world (minus Antarctica)
library(maps)
library(ggplot2)
world_map <- map_data("world") %>% 
  filter(region != "Antarctica") %>% 
  ggplot(aes(x = long, y = lat, group = group)) + 
  geom_polygon(fill = "light blue") + 
  coord_fixed() +
  theme_void()
world_map
  1. Create a basic map of a country, eg UK
library(maps)
library(ggplot2)
UK_map <- map_data("world", region = "UK") %>% 
  ggplot(aes(x = long, y = lat, group = group)) + 
  geom_polygon(fill = "dark blue") + 
  coord_fixed() +
  theme_void()
UK_map
  1. Create a detailed map of a country, eg counties within UK Using sf and ggplot packages as per the following tutorials:
library(maps)
library(tidyverse)
library(sf)

UK_map <- map_data("world", region = "UK") %>%
  
  ggplot(aes(x = long, y = lat, group = group)) + 
  geom_polygon(fill = "dark blue") + 
  coord_fixed() +
  theme_void()
UK_map

Tile grid maps

geofacet

Package for creating tile grid maps for regions including:

  • london_boroughs_grid

  • uk_regions1

  • world_86countries_grid & world_countries_grid1

  • china_prov_grid1

Related: countrycode package

Model

“All models are wrong, but some are useful”

The goal of a model is not to uncover truth, but to discover a simple approximation that is useful (in order to understand the behaviour of something).

  • linear model takes the general form:
y = a_0 + a_1 * x

where

  • a_0 = starting point

  • a_1 = difference between x and y at a given point

(NB this is another way of expressing y = mx + c)

Linear models also assume that the ‘residuals’ (difference between observed and predicted values) have a normal distribution.

lm

or, how to apply a linear model to data

  1. Create a model (a type of list) with the function lm(), where x_variable is the independent ‘cause’ and y_variable is the dependent ‘consequence’:
model_name <- lm(y_variable ~ x_variable, data = datset)
  1. Create a ‘grid’ of your data (evenly spaced grid of points from the data)
grid_name <- data %>% data_grid(x_variable)
  1. Add predictions to your gridded data using the model you created
grid_name <- grid_name %>% add_preidictions(model_name)
  1. To visualise the linear model, do the usual ggplot of the distribution of the dataset, then add a geom_line layer using the grid_name as its data, eg
ggplot(dataset, aes(x_variable, y_variable)) +
  geom_hex(bins = ###) +
  geom_line(grid_name)

geom_smooth

or, modelling with ggplot (notes taken from Data visualization: a practical introduction).

Basic linear model:

geom_smooth(method = "lm")

Specify linear model:

geom_smooth(method = "lm", formula = y_variable ~ x_variable)

Iterate

Using the book ‘R for Data Science’ and the tidyverse package

Functions

  • Functions allow you to automate tasks, as opposed to copy-and-pasting similar bits of code

  • There are huge advantages in using a function rather than copy-and-paste: if you want to make a change, you only need to do it once; and you’re less likely to make typos and mistakes.

  • Use base R to write functions

There are 3 basic building blocks for creating a function.

  1. A name

  2. The inputs (or arguments)

  3. The ‘body’ of the function, ie code that goes between {}

…which comes together as:

function_name <- function(arguments){ function code }

What you get back from the function will by default be the last statement the function evaluates.

BUT you can also choose to use:

  • return() in the middle of the function code to signal to make it easier to understand, eg if you’ve written very long if statements.

  • invisible() to ensure that something doesn’t get printed out as a result of the function, eg a new dataframe that you’re using to create a plot

Examples

If we were to create a function that adds 2 to the input, it would look like this:

add_two <- function(x){
  x+2
}

and you can apply it like this

add_two(2)

you can also apply it to every element of a vector

b <- c(1:5)
add_two(b)

Here’s another example. We can create a fizzbuzz function that takes a single number as input and returns “fizz” if it’s divisible by three and “buzz” if it’s divisible by 5 and “fizzbuzz” if it’s divisible by 3 and 5.

fizzbuzz <- function(x) {
  
  # stopifnot() function ensures the truth of an r expression
  stopifnot(length(x) == 1)
  stopifnot(is.numeric(x))

  # %% and && are logical operators, where %% is 'remainder from division' and && is 'and'  
  if (!(x %% 3) && !(x %% 5)) {
    "fizzbuzz"
  } else if (!(x %% 3)) {
    "fizz"
  } else if (!(x %% 5)) {
    "buzz"
  } else {
    x
  }
}

fizzbuzz(378)

It’s useful to have some naming conventions for your function arguments. The following are recommended:

  • x, y and z for vectors

  • df for data frames

  • i for the row number (indices)

  • j for the column number (indices)

  • n for the number of rows

  • p for the numer of columns

It’s also useful to add a generic error message to your functions with stopifnot(), which you can use to assert what SHOULD be true (rather than checking for what might be wrong). For example:

function_name <- function(x, y, na.rm = FALSE) {
  stopifnot(is.logical(na.rm), length(na.rm) == 1)
  stopifnot(length(x) == length(y))
  
  function code
}

For loops

  • Functions help to reduce duplication of repeated patterns of code

  • Iteration helps to reduce duplication of repeated operations on multiple inputs, eg performing the same operation on different columns or different datasets

  • There are 2 types of iteration:

    1. Imperative programming, eg for loops and while loops

    2. Functional programming means even less duplication than you get in imperative programming

For loops: the basics

  • Every for loop has 3 components:

    1. An output, ie specify what you want the output of your for loop to be, eg a vector and details like its vector type; or you could also specify your output as being a tibble, a factor etc.

    2. A sequence, ie what to loop over & involves assigning i (like ‘it’) to a different value, so for i in _____

    3. The body {}, ie the code that does the work, each time with a different value for i

Examples An example of a simple for loop

output <- vector("double", ncol(mtcars)) # The vector should be of the type 'double' and with the same number of elements as there are columns in mtcars
            names(output) <- names(mtcars) # The names of the elements should be the names of the columns in mtcars
            for (i in names(mtcars)) { # For every column in mtcars
                   output[i] <- mean(mtcars[[i]]) # make the i'th element of the vector output the mean of the i'th column in mtcars (where i'th = 1st, 2nd, 3rd etc)
            }
output # Print output

A similar example that doesn’t require the line names(output) <- names(mtcars) but gives results without column headings would be:

output <- vector("double", ncol(mtcars)) # The vector should be of the type 'double' and with the same number of elements as there are columns in mtcars
            for (i in 1:ncol(mtcars)) { # For every column in the number of columns that mtcars has
                   output[i] <- mean(mtcars[[i]]) # make the i'th element of the vector output the mean of the i'th column in mtcars (where i'th = 1st, 2nd, 3rd etc)
            }
output # Print output

For loops: variations

  • For loops aren’t limited to creating new objects; you can also use them to change existing ones by just removing the assignment at the start

  • For loops can loop over names and values (not just indices / number of columns)

    • loop over elements with for (x in xs)

    • loop over names with for (nm in names(xs))

  • For loops can handle outputs and sequences of unknown length with ‘while’ loops (although this is most useful in context of simulation, so unlikely to need)

Examples

How to load a directory full of csvs into a single data frame using a for loop.

  1. Create a vector with the filename paths
files <- dir("data/", pattern = "\\.csv$", full.names = TRUE)
  1. Use a for loop that preallocates a list to the filenames and loads them in with read_csv()
df <- vector("list"", length(files)) 
for (fname in seq_along(files)) {
df[[i]] <- read_csv(files[[i]])
}
  1. Combine them into a single dataframe
df <- bind_rows(df)

Purrr

  • purrr = better than for loops because it makes code easier to write and to read by focusing on the operation being performed (and not the bookkeeping required to loop over every element and store the output!)

  • Key idea of functional programming = passing one function to another function

  • It eliminates the need for many common for loops and is similar to the apply() family of functions

  • Key idea is to have functions that perform the common pattern of:

    1. Looping over a vector

    2. Doing something to each element

    3. Saving the results

  • There are different purrr functions for different types of output, as follows:

    • map() to make a list

    • map_lgl() to make a logical vector

    • map_int() to make an integer vector

    • map_dbl() to make a double vector

    • map_chr() to make a character vector

  • There are also useful shortcuts you can use within purrr:

    • ~ will replace function(x) when you want to create an anonymous function

    • . will refer to the current list element (a bit like how i works in a for loop)

Here’s the basic structure for using a map function:

map_vectortype(dataset, function)

Examples Calculate the mean of every column in mtcars

map_dbl(mtcars, mean)

Compute the number of unique values in each column of iris

# V1 WITHOUT SHORTCUTS
map_int(iris, function(x) length(unique(x)))
# V2 WITH SHORTCUTS
map_int(iris, ~ length(unique(.)))

Use a map function within a mutate, using a function you’ve created, ie applying a function to every variable within a dataset

# Example dataset
a <- tibble(x = c(1:4), y = c(100:103))
a

# Example function (the old add_two created earlier in the Functions chapter notes)
add_two <- function(x){
  x+2
}

# Example map function
c <- a %>%
  mutate(z = map_dbl(x, add_two),
         z2 = map(x, add_two))
c
c$z2

Apply multiple map functions to nested tibbles, ie applying a set of functions to separate datasets

# Create function, in this case to filter a dataset by Petal.Length > 5
filter_petal <- function(df){
  df %>%
  filter(Petal.Length > 5)
}

# Turn a normal dataframe into a dataframe of nested tibbles, in this case the iris dataset nested by species
iris_mod <- iris %>%
  group_by(Species) %>%
  nest()

# Create 3 new columns in all of the nested tibbles
iris_mod %>%
  
  # Use map to apply the new function filter_petal to each of the separate datasets for Species within iris_mod (We use map() because the output from applying filter_petal to data is a list.)
  mutate(fl_petal = purrr::map(data, filter_petal), 
         
  # If we want to add the number of rows in each dataset, we would then use map_dbl(), because the output from nrow is a single number.
  n_full_data = purrr::map_dbl(data, nrow), 
  
  # Ditto - use map_dbl() for this output, which is the number of rows in our new column, fl_petal, which we created above
  n_petal_large = purrr::map_dbl(fl_petal, nrow)) 

Iterate with ggplot

Add creating a function to repeat the same ggplot & programmatically writing titles, captions etc <<

Iterate with walk

Iteratively save files

Use walk() and its variants walk2 and pwalk() when you want to call a function for its side effects, rather than its return value.

This is the case when you want to render output to the screen or save a file to disk.

For example, if you had a list of plots and a vector of filenames, you can use pwalk() as follows:

# Create a list of multiple plots using the map() function
plots <- mtcars %>% 
          split(.$cyl) %>% 
          map(~ggplot(., aes(mpg, wt)) + geom_point())
plots

# Create an iterative filenaming structure for each plot, in this case the names of the plots followed by .pdf
paths <- stringr::str_c(names(plots), ".pdf")

# Save each plot to the path names defined in 'paths' by combining the plots and the pathnames in a list and saving that list
pwalk(list(paths, plots), ggsave, path = tempdir())

Communicate

RMarkdown

Choose a markdown themes on Bootswatch, including:

  • cosmo ⭐️

  • journal

  • darkly (black)

  • flatly

  • lumen

  • paper

  • readable

  • sandstone

  • simplex

  • slate (grey)

  • superhero (blue)

Create a website in RMarkdown with bookdown

Notes

Factors

  • Factors are used for categories

  • Factors can either have an order that’s “arbitrary” (eg hair colour) or “principled” (eg months)

  • They’re esp useful if you want to display character vectors in non-alphabetical order

  • Use forcats package (not part of tidyverse) for working with factors

For example, say you have a string of months

library(forcats)
x1 <- c("Dec", "Apr", "Jan", "Mar")

To create a “factor” of months over a year:

  1. First create a list of the different months, or levels, in the order they should appear
month_levels <- c("Jan", "Feb", "Mar", "Apr", "May", "Jun",
                  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
  1. Then create a factor with the function factor() and levels =
y1 <- factor(x1, levels = month_levels)

You now have a factor that you can sort according to the order you set with sort()

sort(y1)

You can check the levels in your factor with levels()

levels(y1)

You can reorder your factor in at least 2 ways:

  1. By another variable, eg which month has the most birthdays, with fct_reorder():
fct_reorder(factor, variable)
  1. By moving one or more levels to the end, eg showing “Not applicable” at the end, with fct_relevel():
fct_relevel(factor, "level to pull out", "level to pull out")

You can also edit your factor with fct_recode. This is particularly useful for editing labels for publication. And you can use this technique to combine multiple “old” levels into the same “new” level, eg grouping several levels under “Other”.

data %>% mutate(variable = fct_recode(variable, "new name" = "old name", "new name" = "old name"))

Other modifications you can make to your factor include:

  • fct_collapse(), which is like fct_recode(), but works so that for each new variable you can provide a vector of old levels, eg
data %>% mutate(variable = fct_collapse(variable, "new name" = c("old name", "old name", "old name"))
  • fct_lump, which crudely lumps together all the “small” groups to simplify a plot or table
data %>% mutate(variable = fct_lump(variable))

Vectors

  • Vectors are essentially variables within a tibble

  • There are 3 types of vector:

    1. Atomic which can be further subcategorised into:

      • logical (TRUE, FALSE and NA)

      • integer (numeric without decimal places, eg 243)

      • double (numeric with decimal places, eg 243.5678)

      • character (made up of strings)

      • complex (rarely used during data analysis)

      • raw (ditto)

    2. Lists (aka ‘recursive vectors’ and can contain other lists, so they’re good for representing hierarchies in data)

    3. NULL which is the absence of a vector (and distinct from NA which is the absence of a value in a vector)

Note about integers vs doubles: if you just enter 2 in R, it interprets this as 2.000000, ie a double. To enter 2 as an integer you have to write 2L.

typeof(2)
typeof(2L)
typeof(2.5L)

So what can you do with vectors?

  • You can find out what type of vector a vector is with `typeof()

  • You can find out how many items there are in a vector with `length()

  • You can specify how to read a vector when you import with readr by using col_types specifications, for example:

data <- read_csv("data.csv"), 
              col_types = cols(column_name = col_logical(),
                               column_name = col_integer(),
                               column_name = col_double(),
                               column_name = col_character(),
                               column_name = col_date(format = ""),
                               column_name = col_time(format = ""),
                               column_name = col_datetime(format = ""),
                               column_name = col_number(), # Isn't fussy about numbers containing commas, $ etc
                               column_name = col_skip(), # Skips importing this column
                               column_name = col_guess() # Guesses how to parse based on input
                               )
  • You can convert one type of vector to another with as.logical(), as.integer(), as.double() or as.character()

  • You can perform basic maths operations on vectors, ie don’t need to iterate for every item in the vector

  • You can create and name a vector with c()

Lists

  • Can contain a mix of atomic vector types and other lists

  • It’s useful to use str() because this will give you the structure of your list, rather than the contents

  • To subset items within a list use double brackets [[]] rather than single [].

Augmented vectors

  • Factors, dates, date-times and tibbles are augmented vectors, because they have additional attributes (on top of the usual vector attributes of names, dimensions and class)

    • Factors have a ‘levels’ attribute

    • Dates and date-times have additional ‘class’ attributes

    • Tibbles have additional ‘class’ attributes (tbl_df, tbl and data.frame) as well as column names and row.names

LS0tCnRpdGxlOiAiQ29va2Jvb2siCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGRhcmtseQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKIyBTZXR1cAoKIyMjIEluc3RhbGwKYGBgCmluc3RhbGwucGFja2FnZSgicGFja2FnZV9uYW1lIikKYGBgCgojIyMgTG9hZApgYGAKbGlicmFyeShwYWNrYWdlX25hbWUpCmBgYAoKIyBJbXBvcnQKCiMjIyBGcm9tIGNzdgpgYGAKbGlicmFyeSh0aWR5dmVyc2UpCmRhdGEgPC0gcmVhZF9jc3YoImZvbGRlci9maWxlbmFtZS5jc3YiKQpgYGAKCiMjIyBGcm9tIGEgZ29vZ2xlc2hlZXQKYGBgCmxpYnJhcnkoZ29vZ2xlc2hlZXRzKQpzaGVldCA8LSBnc190aXRsZSjigJxHb29nbGUgc2hlZXQgbmFtZeKAnSkKZGF0YSA8LSBzaGVldCAlPiUgZ3NfcmVhZCh3cyA9IOKAnFdvcmtzaGVldCBuYW1l4oCdKQpgYGAKCiMjIyBGcm9tIGEgdGFibGUgb24gYSB3ZWJwYWdlCmBgYApsaWJyYXJ5KHJ2ZXN0KQp1cmwgPC0gcmVhZF9odG1sKCd1cmwnKQpkYXRhX3JhdyA8LSB1cmwgJT4lCiAgICAgICAgICAgICAgaHRtbF9ub2RlKCJ0YWJsZSIpICU+JQogICAgICAgICAgICAgIGh0bWxfdGFibGUoZmlsbD1UUlVFKQpgYGAKCiMjIyBGcm9tIGEgUERGCi0gVHJ5IGBwZGZ0b29sc2AgcGFja2FnZSAmIFt0aGlzIHR1dG9yaWFsXShodHRwczovL3d3dy5icm9kcmlndWVzLmNvL2Jsb2cvMjAxOC0wNi0xMC1zY3JhcGluZ19wZGZzLz91dG1fY2FtcGFpZ249RGF0YV9FbGl4aXImdXRtX21lZGl1bT1lbWFpbCZ1dG1fc291cmNlPURhdGFfRWxpeGlyXzE4NykKCi0gVXNpbmcgU1BBUlFMIChjb2RlIGZyb20gW1IgYmxvZ2dlcnNdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL3NwYXJxbC13aXRoLXItaW4tbGVzcy10aGFuLTUtbWludXRlcy9hbXAvKSk6CmBgYApsaWJyYXJ5KFNQQVJRTCkgIyBTUEFSUUwgcXVlcnlpbmcgcGFja2FnZQpsaWJyYXJ5KGdncGxvdDIpCgojIFN0ZXAgMSAtIFNldCB1cCBwcmVsaW1pbmFyaWVzIGFuZCBkZWZpbmUgcXVlcnkKIyBEZWZpbmUgdGhlIGRhdGEuZ292IGVuZHBvaW50CmVuZHBvaW50IDwtICJodHRwOi8vc2VydmljZXMuZGF0YS5nb3Yvc3BhcnFsIgoKIyBjcmVhdGUgcXVlcnkgc3RhdGVtZW50CnF1ZXJ5IDwtCiJQUkVGSVggIGRncDExODc6IDxodHRwOi8vZGF0YS1nb3YudHcucnBpLmVkdS92b2NhYi9wLzExODcvPgpTRUxFQ1QgP3llID9maSA/YWMKV0hFUkUgewo/cyBkZ3AxMTg3OnllYXIgP3llIC4KP3MgZGdwMTE4NzpmaXJlcyA/ZmkgLgo/cyBkZ3AxMTg3OmFjcmVzID9hYyAuCn0iCgojIFN0ZXAgMiAtIFVzZSBTUEFSUUwgcGFja2FnZSB0byBzdWJtaXQgcXVlcnkgYW5kIHNhdmUgcmVzdWx0cyB0byBhIGRhdGEgZnJhbWUKcWQgPC0gU1BBUlFMKGVuZHBvaW50LHF1ZXJ5KQpkZiA8LSBxZCRyZXN1bHRzCmBgYAoKIyMjIEZyb20gbXVsdGlwbGUgY3N2cwoKICAxLiBDcmVhdGUgYSB2ZWN0b3Igb2YgYWxsIHRoZSBkYXRhc2V0cwogIApgYGAKICBhbGxfZGF0YXNldHMgPC0gdGliYmxlKCJmaWxlIiA9IGMoJ2RhdGFzZXRfbmFtZV8xJywgJ2RhdGFzZXRfbmFtZV8yJykpIApgYGAKCiAgMi4gQ3JlYXRlIGEgZnVuY3Rpb24gdG8gaW1wb3J0IG9uZSBkYXRhc2V0CmBgYAogIGltcG9ydCA8LSBmdW5jdGlvbihkZil7CiAgICBkZiA8LSByZWFkX2NzdihzdHJfYygiZGYsICIuY3N2IikpCiAgfQpgYGAKCiAgMy4gVXNlIHB1cnJyOjptYXAoKSB0byBydW4gdGhlIGZ1bmN0aW9uIG9uIChlYWNoIGVsZW1lbnQpIG9mIGFsbCB0aGUgZGF0YXNldHMKYGBgCiAgYWxsX2RhdGEgPC0gYWxsX2RhdGFzZXRzICU+JSBtdXRhdGUoZGF0YXNldCA9IHB1cnJyOjptYXAoZmlsZSwgaW1wb3J0KSkKYGBgCiAgCiAgNC4gUnVuIGEgJ2ZvciBsb29wJyB0byBnZXQgUiB0byBhc3NpZ24gZWFjaCB0aWJibGUgdG8gYW4gb2JqZWN0CmBgYAogIGZvcihpIGluIDE6bnJvdyhhbGxfZGF0YSkpewogIGFzc2lnbihhbGxfZGF0YSRmaWxlW1tpXV0sIGFsbF9kYXRhJGRhdGFzZXRbW2ldXSkKICB9CmBgYAogIAojIyMgRnJvbSBtdWx0aXBsZSB3ZWJwYWdlcwoKICAxLiBDcmVhdGUgYSB2ZWN0b3Igd2l0aCB0aGUgVVJMIHBhdGhzIG5lZWRlZCwgZWc6CmBgYAogIHVybF91bmlxdWUgPC0gIyBmdW5jdGlvbiB0byBsaXN0IHRoZXNlLCBlZyB1c2luZyBzZXEoKSBpZiBudW1lcmljYWwKICB1cmxfYmFzZSA8LSAiaHR0cHM6Ly9hYmMiCiAgdXJsX2VuZCA8LSAiL2FiYy5odG1sIgogIHVybF9wYXRoIDwtIHVybF91bmlxdWUgJT4lIHN0cl9jKHVybF9iYXNlLCAuICwgdXJsX2VuZCkKICBkYXRhIDwtIHRpYmJsZSh1cmxfcGF0aCkKYGBgCiAgCiAgMi4gRGVmaW5lIGEgZnVuY3Rpb24gdG8gc2NyYXBlIChhbmQgY2xlYW4pIGRhdGEgZnJvbSBvbmUgdXJsCmBgYAogIHNjcmFwZSA8LSBmdW5jdGlvbih1cmwpewogIHhtbCA8LSByZWFkX2h0bWwodXJsKSAlPiUKICAgICAgICAgIGh0bWxfbm9kZSgidGFibGUiKSAlPiUKICAgICAgICAgIGh0bWxfdGFibGUoZmlsbD1UUlVFKSAlPiUKICB4bWwKYGBgCiAgCiAgMy4gKGlmIG5lZWRlZCkgUHJldmVudCBlcnJvciBtZXNzYWdlcyBmcm9tIFVSTCBwYXRocyB0aGF0IGRvbid0IGV4aXN0CmBgYAogIGV4dHJhY3RfcmVzdWx0cyA8LSBmdW5jdGlvbihkZil7CiAgICBkZiRyZXN1bHQKICB9CmBgYAogIAogIDQuIExvb3Agc2NyYXBlIGZ1bmN0aW9uIG92ZXIgYWxsIFVSTHMgYXMgbmVzdGVkIHRpYmJsZXMKYGBgCiAgZGF0YSA8LSBkYXRhICU+JQogICAgICAgICAgICBtdXRhdGUobmV3X2NvbHVtbiA9IHB1cnJyOjptYXAodXJsX3BhdGgsIHNhZmVseShzY3JhcGUpKSwKICAgICAgICAgICAgbmV3X2NvbHVtbiA9IG1hcChuZXdfY29sdW1uLCBleHRyYWN0X3Jlc3VsdHMpKQpgYGAKCiMjIyBTYXZlIGFzIGNzdgpgYGAKd3JpdGVfY3N2KGRhdGFzZXQsICJmaWxlbmFtZS5jc3YiKQpgYGAKCiMgVGlkeQojIyMgSW5zcGVjdApHZXQgbGlzdCBvZiBhbGwgY29sdW1uIGhlYWRpbmdzIGluIGEgZGF0YXNldApgYGAKY29sbmFtZXMoZGF0YXNldCkKYGBgCgpHZXQgdGhlIG51bWJlciBvZiByb3dzIGFuZCBjb2x1bW5zIGluIGEgZGF0YXNldApgYGAKc3VtbWFyaXNlKCkgCmBgYAoKR2V0IHRoZSBicmVha2Rvd24gb2YgcmVzdWx0cyB3aXRoaW4gYSBjb2x1bW4KYGBgCnRhYmxlKGRhdGFzZXQkY29sdW1uKQpgYGAKCkdldCB0aGUgbnVtYmVyIG9mIHZhbHVlcyBpbiBhIGNvbHVtbgpgYGAKc3VtbWFyeShkYXRhc2V0JGNvbHVtbikKYGBgCgpHZXQgdGhlIG51bWJlciBvZiBOQSB2YWx1ZXMgaW4gYSBjb2x1bW4KYGBgCnN1bShpcy5uYShkYXRhc2V0JGNvbHVtbikpCmBgYAoKIyMjIHRpZHlyCgpXaWRlIC0tPiBza2lubnkKYGBgCmdhdGhlcihkYXRhc2V0LCAibmV3X2NvbHVtbl9uYW1lMSIsICJuZXdfY29sdW1uX25hbWUyIiwgY29sdW1uIHJhbmdlIGVnICcyOjgnKQpgYGAKClNraW5ueSAtLT4gd2lkZQpgYGAKc3ByZWFkKGRhdGFzZXQsICRrZXljb2x1bW4sICR2YWx1ZWNvbHVtbikKYGBgIAoKQ29tYmluZSBkYXRhc2V0cyBieSBhIGNvbW1vbiBjb2x1bW4KYGBgCm1lcmdlKGRhdGFzZXQxLCBkYXRhc2V0MiwgYnkgPSAiY29tbW9uIGNvbHVtbiBuYW1lIikKYGBgIAoKQ29tYmluZSBkYXRhc2V0cyBieSBhdHRhY2hpbmcgcm93cwpgYGAKYXBwZW5kKGRhdGFzZXQxLCBkYXRhc2V0MikKYGBgIAoKQ3JlYXRlIGEgbmV3IGNvbHVtbgpgYGAKbXV0YXRlKG9sZF92YXJpYWJsZSwgbmV3X3ZhcmlhYmxlID0gKGNhbGN1bGF0aW9uKSkKYGBgCgpSZW5hbWUgYSBjb2x1bW4KYGBgCnJlbmFtZShkYXRhc2V0LCBuZXcgbmFtZSA9IG9sZCBuYW1lKQpgYGAKClJlbmFtZSBjYXRlZ29yaWVzIHdpdGhpbiBhIGNvbHVtbgpgYGAKZGF0YSAlPiUgCiAgbXV0YXRlKHZhcmlhYmxlID0gZmN0X3JlY29kZSh2YXJpYWJsZSwgIm5ldyBuYW1lIiA9ICJvbGQgbmFtZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuZXcgbmFtZSA9ICJvbGQgbmFtZSIpKQpgYGAKCkFsdGVybmF0aXZlcyAobW9yZSBmb3IgY29ycmVjdGluZyB0eXBvcyBsaWtlIEZpbmQgJiBSZXBsYWNlKToKICBgYGAKICBkYXRhc2V0JGNvbHVtbltkYXRhc2V0JGNvbHVtbiA9PSAib2xkIl0gPC0gIm5ldyIKICBgYGAKCiAgYGBgCiAgIm5ld19uYW1lIiA8LSBsZW5ndGgoZ3JlcCgiZXhpc3RpbmdfbGFiZWwiLCBkYXRhc2V0JGNvbHVtbikpCiAgYGBgCgpEcm9wIGNvbHVtbihzKSAvIHJvdyhzKQpgYGAgCnN1YnNldChkYXRhc2V0LCBzZWxlY3QgPSAtYyhjb2x1bW5zKSkKc3Vic2V0KGRhdGFzZXQsIGNvbHVtbiAhPSAiIikKYGBgIAoKUmVtb3ZlIE5BcwpgYGAKbmEub21pdChkYXRhc2V0KQpgYGAgCgpBcnJhbmdlIGEgY29sdW1uIGluIGRlc2NlbmRpbmcgb3JkZXIKYGBgCmFycmFuZ2UoZGF0YXNldCwgZGVzYyhjb2x1bW4pKSkKYGBgCgojIyMgc3RyaW5ncgoKKipGaWx0ZXIgYSBjb2x1bW4gaWYgY29udGFpbnMgdGV4dCBzdHJpbmcqKgoKYGBgCmdyZXBsKOKAnHRleHQgc3RyaW5n4oCdLCB2YXJpYWJsZSkKYGBgIAoKKipSZW1vdmUgc3ltYm9scywgZWcgJSBzaWduKioKCmBgYApkYXRhc2V0JGNvbHVtbiA8LSBnc3ViKCJcXCUiLCIiLCBkYXRhc2V0JGNvbHVtbikKYGBgCgoqKkNvbnZlcnQgY2hhcmFjdGVyIGNvbHVtbiB0byBudW1lcmljKioKCmBgYApkYXRhc2V0JGNvbHVtbiA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihkYXRhc2V0JGNvbHVtbikpCmBgYAoKKipSb3VuZCBudW1iZXJzKioKCmBgYAptdXRhdGUodmFyaWFibGUgPSByb3VuZCh2YXJpYWJsZSkpCmBgYAoKIyMjIFtqYW5pdG9yXShodHRwczovL2dpdGh1Yi5jb20vc2ZpcmtlL2phbml0b3IpCgoqKkNsZWFuIGNvbHVtbiBoZWFkaW5nIG5hbWVzICh0byBsb3dlcmNhc2Ugd2l0aCB1bmRlcnNjb3JlcyoqCgpgYGAKY2xlYW5fbmFtZXMoKQpgYGAKCioqQ3JlYXRlIGEgcXVpY2sgcGl2b3QgdGFibGUgdG8gc3VtbWFyaXNlIHZhcmlhYmxlcyoqCgpgYGAKdGFieWwodmFyaWFibGUxLCB2YXJpYWJsZTIpICU+JSAKYWRvcm5fKgpgYGAKCioqUmVtb3ZlIGVtcHR5IHJvd3MgYW5kIGNvbHVtbnMqKgoKYGBgCnJlbW92ZV9lbXB0eSgpIApgYGAKCioqRml4IGRhdGVzIHN0b3JlZCBhcyBzZXJpYWwgbnVtYmVycyoqCgpgYGAKZXhjZWxfbnVtZXJpY190b19kYXRlKCkKYGBgCgojIFRyYW5zZm9ybQojIyMgZHBseXIKClNlbGVjdCBjb2x1bW5zCgpgYGAKc2VsZWN0KHZhcmlhYmxlLCB2YXJpYWJsZSwgdmFyaWFibGUpCmBgYAoKU2VsZWN0IHJvd3MgdGhhdCBtZWV0IGNyaXRlcmlhCgpgYGAKZmlsdGVyKHZhcmlhYmxlID09ICIuLi4iIHwgdmFyaWFibGUgPT0gIi4uLiIpCmBgYAoKQW5hbHlzZSAobGlrZSBhIHBpdm90IHRhYmxlKQoKYGBgCmdyb3VwX2J5KGNvbHVtbikgJT4lIAogIHN1bW1hcmlzZShjYWxjdWxhdGlvbikKYGBgCgoqKkNsYXNzaWMgY29kZSBzdHJ1Y3R1cmVzKioKCi0gUGl2b3QgYW5kIHBlcmZvcm0gY2FsY3VsYXRpb25zIG9uIGRhdGEKCmBgYApkYXRhX2FuYWx5c2lzIDwtIHJhd19kYXRhICU+JQogICAgICAgICAgCSAgICAgICAgICAgZmlsdGVyKCksCiAgICAgICAgICAJICAgICAgICAgICBncm91cF9ieSgpLAogICAgICAgICAgCSAgICAgICAgICAgc3VtbWFyaXNlKCkKYGBgCi4uLndoaWNoIHNvdW5kcyBsaWtlOgoiVGhlIG9yaWdpbmFsIGRhdGFmcmFtZSBpcyBYWFgsCglub3cgZmlsdGVyIGRhdGEgdG8gb25seSBpbmNsdWRlIHJvd3MgdGhhdCBzYXRpc2Z5IHRoZSBjb25kaXRpb25zIFlZWSwKCW5vdyBncm91cCB0aGUgZGF0YSBhdCBlYWNoIGxldmVsIG9mIHRoZSB2YXJpYWJsZShzKSBaWlosCglub3cgc3VtbWFyaXplIHRoZSBkYXRhIGFuZCBjYWxjdWxhdGUgc3VtbWFyeSBmdW5jdGlvbnMgWFhY4oCmIgoKLSBQaXZvdCBkYXRhIHRvIHNob3cgcGVyY2VudGFnZSBicmVha2Rvd24gb2YgZWFjaCBjb2x1bW4KCmBgYApkYXRhX2FuYWx5c2lzIDwtIHJhd19kYXRhICU+JQogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwXyBieShjb2x1bW4gMSwgY29sdW1uIDIpICU+JQogICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShuID0gbigpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUocGN0ID0gbiAvIHN1bShuKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgc3ByZWFkKGNvbHVtbiAyLCBwY3QpCmBgYAoKKipDYWxjdWxhdGlvbnMqKgoKYGBgCnN1bSgpCmNvdW50KCkKdW5pcXVlKCkKbl9kaXN0aW5jdCgpCmBgYAoKQ291bnQgdW5pcXVlIHZhbHVlcyBpbiBhIGNvbHVtbiBhbmQgYXJyYW5nZSBpbiBkZXNjZW5kaW5nIG9yZGVyIHdpdGgKYGBgCmRhdGEgJT4lIGNvdW50KGNvbHVtbikgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgojIyMgYXBwbHkKClBpdm90IG9uZSBjb2x1bW4gb2YgZGF0YSAodXNlIDIgZm9yIHJvd3MpIHdpdGggCmBgYApzYXBwbHkoZGF0YXNldCwgZnVuY3Rpb24sIDEpCmBgYAoKR2V0IHRoZSBudW1iZXIgb2YgdW5pcXVlIHZhbHVlcyBpbiBldmVyeSBjb2x1bW4KYGBgCnJhcHBseShkYXRhLGZ1bmN0aW9uKHgpbGVuZ3RoKHVuaXF1ZSh4KSkpCmBgYAoKIyMjIHN0cmluZ3IKCkpPSU4gd2l0aCBgc3RyX2MoKWAKCi0gYHNlcCA9YCBob3cgeW91IGNvbWJpbmUgdGhlIGNvbHVtbnMsIGVnIHNlcCA9ICIsIiBmb3IgY29tbWEgc2VwYXJhdGVkIHZhbHVlcwoKLSBgY29sbGFwc2UgPWAgaG93IHlvdSBjb21iaW5lIHRoZSByb3dzLCBlZyBjb2xsYXBzZSA9ICItIiwgdG8gYWRkIGEgaHlwaGVuIGJldHdlZW4gZWFjaCB2ZWN0b3IKClNQTElUIHdpdGggYHN0cl9zcGxpdCgpYAoKR0VUIE1BVENIRVMKCi0gYHN0cl92aWV3KClgIHRvIHNlZSByZXN1bHRzIGluIFZpZXdlciB3aW5kb3cKCi0gYHN0cl9kZXRlY3QoKWAgdG8gZ2V0IFRSVUUvRkFMU0UgcmVzcG9uc2UKCi0gYHN0cl9jb3VudCgpYCB0byBnZXQgY291bnRlZCByZXNwb25zZQoKR0VUIE1BVENIRVMgV0hFTiBXT1JLSU5HIFdJVEggQSBUQUJMRSB3aXRoIGB0aWR5cjo6ZXh0cmFjdChvbGRfY29sdW1uLCAibmV3X2NvbHVtbiIsICJyZWd1bGFyIGV4cHJlc3Npb24iKWAKCkdFVCBMT0NBVElPTiB3aXRoIGBzdHJfbG9jYXRlKClgCgpTVUJTRVQgd2l0aCBgc3RyX3N1YigpYAoKQ09VTlQgTEVOR1RIIHdpdGggYHN0cl9sZW5ndGgoKWAKCldSQVAgd2l0aCBgc3RyX3dyYXBgCgotIGB3aWR0aCA9YCBsaW5lIHdpZHRoIGluIGNoYXJhY3RlcnMKCi0gYGluZGVudCA9YCBpbmRlbnRhdGlvbiBvZiBmaXJzdCBsaW5lIGluIGVhY2ggcGFyYWdyYXBoCgotIGBleGRlbnQgPWAgaW5kZW50YXRpb24gb2YgZm9sbG93aW5nIGxpbmVzIGluIGVhY2ggcGFyYWdyYXBoCgojIyMgcmVnZXgKYWthICJyZWd1bGFyIGV4cHJlc3Npb25zIgoKCS0gYC5gID0gYW55IGNoYXJhY3RlciAod2lsZGNhcmQpCgkKCS0gYF5gID0gc3RhcnQgb2YgdGhlIHN0cmluZwoJCgktIGAkYCA9IGVuZCBvZiB0aGUgc3RyaW5nCgkKCS0gYG1hdGNoID0gVFJVRWAgPSB2aWV3IGp1c3QgdGhvc2UgbWF0Y2hlcyB0aGF0IG1lZXQgY3JpdGVyaWEKCQoJLSBgXGQ6YCBhbnkgZGlnaXQKCQoJLSBgXHM6YCBhbnkgd2hpdGVzcGFjZSAoc3BhY2UsIHRhYiwgbmV3bGluZSkKCQoJLSBgW2FiY106YCBtYXRjaGVzIGEsIGIsIG9yIGMKCQoJLSBgW15hYmNdOmAgbWF0Y2hlcyBhbnl0aGluZyBleGNlcHQgYSwgYiwgb3IgYwoKIyBWaXN1YWxpc2Ug8J+kqSAKCiMjIyBCYXNpY3MKCkNsYXNzaWMgY29kZSBzdHJ1Y3R1cmUKYGBgCmdncGxvdChkYXRhc2V0KSArCgkgdml6X2Z1bmN0aW9uKGFlcyh4ID0gdmFyaWFibGUsCgkJCQkgICAgICAgICAgICB5ID0gdmFyaWFibGUsCgkJCQkgICAgICAgICAgICBncm91cCA9IHZhcmlhYmxlLAogICAgICAgICAgICAgICAgICAgIHNpemUgPSB2YXJpYWJsZSwKICAgICAgICAgICAgICAgICAgICBmaWxsID0gdmFyaWFibGUpICsKICAgc2NhbGVfeC95X2Rpc2NyZXRlL2NvbnRpbnVvdXMoYnJlYWtzID0gYygpLCAKICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoKSwgCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCkpICsKICAgbGFicyh0aXRsZSA9ICIuLi4iLAogICAgICAgIHN1YnRpdGxlID0gIi4uLiIsCiAgICAgICAgeCA9ICIuLi4iLAogICAgICAgIHkgPSAiLi4uIiwKICAgICAgICBjYXB0aW9uID0gIi4uLiIpICsKICAgdGhlbWUoKSArCiAgIGdnc2F2ZSgibmFtZS5maWxldHlwZSIsIGhlaWdodCA9ICwgd2lkdGggPSAsIHVuaXQgPSAiIikKYGBgCgpBZGRpdGlvbmFsIGxheWVycwpgYGAKZ2VvbV9obGluZSgpIApnZW9tX3ZsaW5lKCkKZ2VvbV9zZWdtZW50KCkKZ2VvbV90ZXh0KCkKZ2VvbV9yZWN0KCkKYGBgCgpUcmFuc2Zvcm0gZGF0YSB3aXRoaW4gYSBnZ3Bsb3QgZnVuY3Rpb24KYGBgCnBsb3QxIDwtIGdncGxvdChmaWx0ZXIoZGF0YXNldCwgdmFyaWFibGUgPT0gY3JpdGVyaWEpKQpgYGAKCiMjIyBMZWdlbmRzCgpSZW9yZGVyCgogIDEuIEZpcnN0IGNyZWF0ZSBhIGZhY3RvcjoKICBgYGAKICBkYXRhc2V0JG5ld2NvbHVtbiA8LSBmYWN0b3IoZGF0YXNldCRjb2x1bW4sICBsZXZlbHMgPSBjKOKAnGl0ZW0x4oCdLCDigJxpdGVtMuKAnSkgLCBvcmRlcmVkID0gVFJVRSkKICBgYGAKICAKICAyLiBUaGVuIGluIGdncGxvdCgpIGFlc3RoZXRpYyBtYXBwaW5ncywgdXNlIHRoaXMgbmV3IGNvbHVtbiwgcmF0aGVyIHRoYW4gdGhlIHByZS1leGlzdGluZyBvbmUuCgpSZW1vdmUKYGBgCnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgIAoKIyMjIEF4ZXMKCkFycmFuZ2UgaW4gZGVzY2VuZGluZyBvcmRlcgpgYGAKZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih2YXJpYWJsZTEsIC12YXJpYWJsZTIpLCB5ID0gdmFyaWFibGUyKQpgYGAKCiMjIyBUZXh0CgpSZW1vdmUgLyBzdHlsZSBheGVzIGxhYmVscwpgYGAKYXhpcy50aXRsZS54L3kgPSBlbGVtZW50X2JsYW5rKCkgb3IgZWxlbWVudF90ZXh0KHNpemUgPSAsIGZhbWlseSA9ICwgZm9udCA9ICkKYXhpcy50ZXh0LngveSA9IGVsZW1lbnRfYmxhbmsoKSBvciBlbGVtZW50X3RleHQoc2l6ZSA9ICwgZmFtaWx5ID0gLCBmb250ID0gKQpgYGAKCldyYXAgbG9uZyBsYWJlbHMKYGBgCnNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGggPSAxMCkpCmBgYAoKUm90YXRlIGxhYmVscyA0NSBkZWdyZWVzCmBgYAp0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUpKQpgYGAgCgojIyMgVGhlbWVzCgpGcm9tIGdncGxvdAoKLSBgdGhlbWVfZ3JheSgpYCDigJMgc2lnbmF0dXJlIGdncGxvdDIgdGhlbWUKCi0gYHRoZW1lX2J3KClgIOKAkyBkYXJrIG9uIGxpZ2h0IGdncGxvdDIgdGhlbWUKCi0gYHRoZW1lX2xpbmVkcmF3KClgIOKAkyB1c2VzIGJsYWNrIGxpbmVzIG9uIHdoaXRlIGJhY2tncm91bmRzIG9ubHkKCi0gYHRoZW1lX2xpZ2h0KClgIOKAkyBzaW1pbGFyIHRvIGxpbmVkcmF3KCkgYnV0IHdpdGggZ3JleSBsaW5lcyBhc3dlbGwKCi0gYHRoZW1lX2RhcmsoKWAg4oCTIGxpbmVzIG9uIGEgZGFyayBiYWNrZ3JvdW5kIGluc3RlYWQgb2YgbGlnaHQKCi0gYHRoZW1lX21pbmltYWwoKWAg4oCTIG5vIGJhY2tncm91bmQgYW5ub3RhdGlvbnMsIG1pbmltYWwgZmVlbC4KCi0gYHRoZW1lX2NsYXNzaWMoKWAg4oCTIHRoZW1lIHdpdGggbm8gZ3JpZCBsaW5lcwoKLSBgdGhlbWVfdm9pZCgpYCDigJMgZW1wdHkgdGhlbWUgd2l0aCBubyBlbGVtZW50cwoKRnJvbSBnZ3RoZW1lcwoKLSBgdGhlbWVfc29saWQoKWAgLSBUaGVtZSB3aXRoIG5vdGhpbmcgb3RoZXIgdGhhbiBhIGJhY2tncm91bmQgY29sb3IKCi0gYHRoZW1lX21hcCgpYCAtIENsZWFuIHRoZW1lIGZvciBtYXBzCgotIGB0aGVtZV9pZ3JheSgpYCAtICBJbnZlcnNlIGdyYXkgdGhlbWUKCi0gYHRoZW1lX2Vjb25vbWlzdCgpYAoKLSBgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KClgCgotIGB0aGVtZV93c2ooKWAgCgotIGB0aGVtZV9mZXcoKWAKCi0gYHRoZW1lX3R1ZnRlKClgCgotIGB0aGVtZV9leGNlbCgpYAoKLSBgdGhlbWVfZ2RvY3MoKWAKCi0gYHRoZW1lX3N0YXRhKClgIAoKLSBgdGhlbWVfc29sYXJpemVkKClgCgotIGB0aGVtZV9oYygpYCAtIEhpZ2hjaGFydHMgSlMgdGhlbWUKCiMjIyBJbnRlcmFjdGl2ZSBnZ3Bsb3RzCgoxLiAqKmlyYXBoKioKICBgYGAKICBsaWJyYXJ5KGlyYXBoKQogIGdncGxvdChkYXRhLCBhZXMoeCA9ICwgeSA9ICwgdG9vbHRpcCA9IHZhcmlhYmxlKSArCiAgICAgIGdlb21fPz8/X2ludGVyYWN0aXZlKCkKICBgYGAKCjIuICoqcGxvdGx5KioKICBgYGAKICBsaWJyYXJ5KHBsb3RseSkKICBnZ3Bsb3RseShwbG90LCB0b29sdGlwID0gdmFyaWFibGUpCiAgYGBgCgozLiAqKltoaWdoY2hhcnRlcl0oaHR0cDovL2prdW5zdC5jb20vaGlnaGNoYXJ0ZXIvaW5kZXguaHRtbCkqKgoKNC4gKipbaHRtbHdpZGdldHNdKGh0dHA6Ly93d3cuaHRtbHdpZGdldHMub3JnLy5odG1sKSoqCgo1LiAqKlJTaGlueSoqCgogIC0gVG8gbWFrZSBhIHNoaW55IGFwcCwgeW91IG5lZWQgdG8gY3JlYXRlIHNjcmlwdCB3aXRoIGEgZmlsZW5hbWUgZW5kaW5nICJhcHAuUiIKICAKICAtIFRoZSBiYXNpYyBzdHJ1Y3R1cmUgb2YgYW4gUiBTaGlueSBhcHA6CiAgCiAgYGBgCiAgbGlicmFyeShzaGlueSkKICB1aSA8LSBmbHVpZFBhZ2UoKQogIGlucHV0KCkgZnVuY3Rpb25zLCBlZyBzbGlkZXJJbnB1dCgpCiAgb3V0cHV0KCkgZnVuY3Rpb25zLCBlZyBwbG90T3V0cHV0KCkKICAKICBzZXJ2ZXIgPC0gZnVuY3Rpb24oaW5wdXQsIG91dHB1dCkgeyB9CiAgb3V0cHV0JHh4eHggPC0gcmVuZGVyKigpLCBlZyByZW5kZXJQbG90KCkKICAKICBzaGlueUFwcCh1aSA9IHVpLCBzZXJ2ZXIgPSBzZXJ2ZXIpCiAgYGBgCgojIyMgR2VvZ3JhcGhpY2FsIG1hcHMKCkludHJvZHVjdGlvbgoKVGhlcmUgc2VlbXMgdG8gYmUgbG90cyBvZiBkaWZmZXJlbnQgd2F5cyB5b3UgY2FuIG1ha2UgbWFwcyBpbiBSOiBwYXJ0bHkgYmVjYXVzZSB0aGUgYmVzdCBhcHByb2FjaCB3aWxsIGRlcGVuZCBvbiB3aGF0IHR5cGUgb2YgbWFwIHlvdSdyZSBtYWtpbmc7IGFuZCBwYXJ0bHkganVzdCBiZWNhdXNlIHRoZXJlJ3MgbG90cyBvZiBjb21wZXRpbmcgcGFja2FnZXMgYXZhaWxhYmxlLgoKLSBXYXlzIG9mIGdldHRpbmcgbWFwIGRhdGEKCiAgICAxLiBWZWN0b3IgaW1hZ2UgbWFwcywgZWcgc2YgcGFja2FnZSBvciBtYXBzIHBhY2thZ2UgYW5kIG1hcF9kYXRhKCkgZnVuY3Rpb24KCiAgICAyLiBSYXN0ZXIgaW1hZ2UgbWFwcywgZWcgcmFzdGVyIHBhY2thZ2UKCi0gUGFja2FnZXMgZm9yIHBsb3R0aW5nIG1hcCBkYXRhCgogICAgMS4gZ2dwbG90LCBlZyBnZW9tX3BvbHlnb24oKSBhbmQgZ2VvbV9zZigpCgogICAgMi4gdG1hcCBhbmQgdG1hcHRvb2xzIChkdW5ubykKCiAgICAzLiBsZWFmbGV0IChkdW5ubykKCioqQSBub3RlIG9uIHNoYXBlZmlsZXMqKgoKLSBSYXN0ZXIKCiAgLSBzdGF0aWMgaW1hZ2UgZmlsZXMgZ2VuZXJhdGVkIHByZXZpb3VzbHkgYnkgdGhlIG1hcHBpbmcgc2VydmljZSwgd2hpY2ggbGltaXRzIHlvdXIgYWJpbGl0eSB0byByZWRyYXcgb3IgY2hhbmdlIHRoZSBhcHBlYXJhbmNlIG9mIHRoZSBnZW9ncmFwaGljIG1hcAoKICAtIHRyYWRlb2ZmIG1lYW5zIHlvdSBjYW4gaW1tZWRpYXRlbHkgZm9jdXMgb24gaW5jb3Jwb3JhdGluZyBhZGRpdGlvbmFsIGRhdGEgaW50byB0aGUgbWFwOyBiZXR0ZXIgZm9yIGdyaWRkZWQgZGF0YSwgbGlrZSBzYXRlbGxpdGUgaW1hZ2VyeQoKLSBWZWN0b3IgCgogIC0gc3BhdGlhbCBkYXRhIGZpbGVzIHdoaWNoIGNvbnRhaW4gZGV0YWlsZWQgaW5mb3JtYXRpb24gbmVjZXNzYXJ5IHRvIGRyYXcgYWxsIHRoZSBjb21wb25lbnRzIG9mIGEgbWFwIChlLmcuIHBvaW50cywgbGluZXMsIHBvbHlnb25zKQoKICAtIHJlcXVpcmVzIGltcG9ydGluZyB0aGlzIGRhdGEgaW50byBSOyBvbmNlIGltcG9ydGVkIHRob3VnaCwgZ2dwbG90MiB3b3JrcyB3aXRoIHNpbXBsZSBmZWF0dXJlcyAoc2YpIGRhdGEgZnJhbWVzIHRvIGVhc2lseSBnZW5lcmF0ZSBnZW9zcGF0aWFsIHZpc3VhbGl6YXRpb25zIHVzaW5nIGFsbCB0aGUgY29yZSBlbGVtZW50cyBhbmQgYXBwcm9hY2hlcyBvZiBnZ3Bsb3QoKQoKLSBXaGVyZSB0byBnZXQgc2hhcGVmaWxlczogaHR0cDovL3d3dy5uYXR1cmFsZWFydGhkYXRhLmNvbS9kb3dubG9hZHMvCgpFeGFtcGxlcwoKMS4gQ3JlYXRlIGEgYmFzaWMgbWFwIG9mIHRoZSB3b3JsZCAobWludXMgQW50YXJjdGljYSkKYGBge3Igd29ybGQgbWFwfQpsaWJyYXJ5KG1hcHMpCmxpYnJhcnkoZ2dwbG90MikKd29ybGRfbWFwIDwtIG1hcF9kYXRhKCJ3b3JsZCIpICU+JSAKICBmaWx0ZXIocmVnaW9uICE9ICJBbnRhcmN0aWNhIikgJT4lIAogIGdncGxvdChhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKSArIAogIGdlb21fcG9seWdvbihmaWxsID0gImxpZ2h0IGJsdWUiKSArIAogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX3ZvaWQoKQp3b3JsZF9tYXAKYGBgCgoyLiBDcmVhdGUgYSBiYXNpYyBtYXAgb2YgYSBjb3VudHJ5LCBlZyBVSwpgYGB7ciBjb3VudHJ5IG1hcH0KbGlicmFyeShtYXBzKQpsaWJyYXJ5KGdncGxvdDIpClVLX21hcCA8LSBtYXBfZGF0YSgid29ybGQiLCByZWdpb24gPSAiVUsiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsgCiAgZ2VvbV9wb2x5Z29uKGZpbGwgPSAiZGFyayBibHVlIikgKyAKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV92b2lkKCkKVUtfbWFwCmBgYAoKMy4gQ3JlYXRlIGEgZGV0YWlsZWQgbWFwIG9mIGEgY291bnRyeSwgZWcgY291bnRpZXMgd2l0aGluIFVLIApVc2luZyBzZiBhbmQgZ2dwbG90IHBhY2thZ2VzIGFzIHBlciB0aGUgZm9sbG93aW5nIHR1dG9yaWFsczoKCi0gaHR0cHM6Ly93d3cuci1zcGF0aWFsLm9yZy9yLzIwMTgvMTAvMjUvZ2dwbG90Mi1zZi5odG1sCgotIGh0dHBzOi8vY2Zzcy51Y2hpY2Fnby5lZHUvZ2Vvdml6X3Bsb3QuaHRtbApgYGB7ciBzaGFwZWZpbGUgbWFwfQpsaWJyYXJ5KG1hcHMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQoKVUtfbWFwIDwtIG1hcF9kYXRhKCJ3b3JsZCIsIHJlZ2lvbiA9ICJVSyIpICU+JQogIAogIGdncGxvdChhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKSArIAogIGdlb21fcG9seWdvbihmaWxsID0gImRhcmsgYmx1ZSIpICsgCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfdm9pZCgpClVLX21hcApgYGAKCiMjIyBUaWxlIGdyaWQgbWFwcwoKIyMjIyBbZ2VvZmFjZXRdKGh0dHBzOi8vaGFmZW4uZ2l0aHViLmlvL2dlb2ZhY2V0LykKClBhY2thZ2UgZm9yIGNyZWF0aW5nIHRpbGUgZ3JpZCBtYXBzIGZvciByZWdpb25zIGluY2x1ZGluZzoKCi0gYGxvbmRvbl9ib3JvdWdoc19ncmlkYAoKLSBgdWtfcmVnaW9uczFgIAoKLSBgd29ybGRfODZjb3VudHJpZXNfZ3JpZGAgJiBgd29ybGRfY291bnRyaWVzX2dyaWQxYAoKLSBgY2hpbmFfcHJvdl9ncmlkMWAKClJlbGF0ZWQ6IFtjb3VudHJ5Y29kZV0oaHR0cHM6Ly9naXRodWIuY29tL3ZpbmNlbnRhcmVsYnVuZG9jay9jb3VudHJ5Y29kZSkgcGFja2FnZQoKIyBNb2RlbAoKKioiQWxsIG1vZGVscyBhcmUgd3JvbmcsIGJ1dCBzb21lIGFyZSB1c2VmdWwiKioKClRoZSBnb2FsIG9mIGEgbW9kZWwgaXMgbm90IHRvIHVuY292ZXIgdHJ1dGgsIGJ1dCB0byBkaXNjb3ZlciBhIHNpbXBsZSBhcHByb3hpbWF0aW9uIHRoYXQgaXMgdXNlZnVsIChpbiBvcmRlciB0byB1bmRlcnN0YW5kIHRoZSBiZWhhdmlvdXIgb2Ygc29tZXRoaW5nKS4KCiogbCoqaW5lYXIgKiptb2RlbCB0YWtlcyB0aGUgZ2VuZXJhbCBmb3JtOgoKYGBgCnkgPSBhXzAgKyBhXzEgKiB4CmBgYAoKd2hlcmUgCgotIGBhXzBgID0gc3RhcnRpbmcgcG9pbnQKCi0gYGFfMWAgPSBkaWZmZXJlbmNlIGJldHdlZW4geCBhbmQgeSBhdCBhIGdpdmVuIHBvaW50CgooTkIgdGhpcyBpcyBhbm90aGVyIHdheSBvZiBleHByZXNzaW5nIGB5ID0gbXggKyBjYCkKCkxpbmVhciBtb2RlbHMgYWxzbyBhc3N1bWUgdGhhdCB0aGUgJ3Jlc2lkdWFscycgKGRpZmZlcmVuY2UgYmV0d2VlbiBvYnNlcnZlZCBhbmQgcHJlZGljdGVkIHZhbHVlcykgaGF2ZSBhIG5vcm1hbCBkaXN0cmlidXRpb24uCgojIyMgbG0KCm9yLCBob3cgdG8gYXBwbHkgYSBsaW5lYXIgbW9kZWwgdG8gZGF0YQoKMS4gQ3JlYXRlIGEgbW9kZWwgKGEgdHlwZSBvZiBsaXN0KSB3aXRoIHRoZSBmdW5jdGlvbiBgbG0oKWAsIHdoZXJlIGB4X3ZhcmlhYmxlYCBpcyB0aGUgaW5kZXBlbmRlbnQgJ2NhdXNlJyBhbmQgYHlfdmFyaWFibGVgIGlzIHRoZSBkZXBlbmRlbnQgJ2NvbnNlcXVlbmNlJzoKCmBgYAptb2RlbF9uYW1lIDwtIGxtKHlfdmFyaWFibGUgfiB4X3ZhcmlhYmxlLCBkYXRhID0gZGF0c2V0KQpgYGAKCjIuIENyZWF0ZSBhICdncmlkJyBvZiB5b3VyIGRhdGEgKGV2ZW5seSBzcGFjZWQgZ3JpZCBvZiBwb2ludHMgZnJvbSB0aGUgZGF0YSkKCmBgYApncmlkX25hbWUgPC0gZGF0YSAlPiUgZGF0YV9ncmlkKHhfdmFyaWFibGUpCmBgYAoKMy4gQWRkIHByZWRpY3Rpb25zIHRvIHlvdXIgZ3JpZGRlZCBkYXRhIHVzaW5nIHRoZSBtb2RlbCB5b3UgY3JlYXRlZAoKYGBgCmdyaWRfbmFtZSA8LSBncmlkX25hbWUgJT4lIGFkZF9wcmVpZGljdGlvbnMobW9kZWxfbmFtZSkKYGBgCgo0LiBUbyB2aXN1YWxpc2UgdGhlIGxpbmVhciBtb2RlbCwgZG8gdGhlIHVzdWFsIGdncGxvdCBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBgZGF0YXNldGAsIHRoZW4gYWRkIGEgYGdlb21fbGluZWAgbGF5ZXIgdXNpbmcgdGhlIGBncmlkX25hbWVgIGFzIGl0cyBkYXRhLCBlZwoKYGBgCmdncGxvdChkYXRhc2V0LCBhZXMoeF92YXJpYWJsZSwgeV92YXJpYWJsZSkpICsKICBnZW9tX2hleChiaW5zID0gIyMjKSArCiAgZ2VvbV9saW5lKGdyaWRfbmFtZSkKYGBgCgojIyMgZ2VvbV9zbW9vdGgKCm9yLCBtb2RlbGxpbmcgd2l0aCBnZ3Bsb3QgKG5vdGVzIHRha2VuIGZyb20gW0RhdGEgdmlzdWFsaXphdGlvbjogYSBwcmFjdGljYWwgaW50cm9kdWN0aW9uXShodHRwczovL3NvY3Zpei5jby9tb2RlbGluZy5odG1sKSkuCgpCYXNpYyBsaW5lYXIgbW9kZWw6CmBgYApnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQpgYGAKClNwZWNpZnkgbGluZWFyIG1vZGVsOgpgYGAKZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHlfdmFyaWFibGUgfiB4X3ZhcmlhYmxlKQpgYGAKCiMgSXRlcmF0ZQoKVXNpbmcgdGhlIGJvb2sgJ1IgZm9yIERhdGEgU2NpZW5jZScgYW5kIHRoZSB0aWR5dmVyc2UgcGFja2FnZQoKIyMjIEZ1bmN0aW9ucwoKLSBGdW5jdGlvbnMgYWxsb3cgeW91IHRvIGF1dG9tYXRlIHRhc2tzLCBhcyBvcHBvc2VkIHRvIGNvcHktYW5kLXBhc3Rpbmcgc2ltaWxhciBiaXRzIG9mIGNvZGUKCi0gVGhlcmUgYXJlIGh1Z2UgYWR2YW50YWdlcyBpbiB1c2luZyBhIGZ1bmN0aW9uIHJhdGhlciB0aGFuIGNvcHktYW5kLXBhc3RlOiBpZiB5b3Ugd2FudCB0byBtYWtlIGEgY2hhbmdlLCB5b3Ugb25seSBuZWVkIHRvIGRvIGl0IG9uY2U7IGFuZCB5b3UncmUgbGVzcyBsaWtlbHkgdG8gbWFrZSB0eXBvcyBhbmQgbWlzdGFrZXMuCgotIFVzZSBiYXNlIFIgdG8gd3JpdGUgZnVuY3Rpb25zCgpUaGVyZSBhcmUgKiozIGJhc2ljIGJ1aWxkaW5nIGJsb2NrcyoqIGZvciBjcmVhdGluZyBhIGZ1bmN0aW9uLgoKMS4gQSBuYW1lCgoyLiBUaGUgaW5wdXRzIChvciBhcmd1bWVudHMpCgozLiBUaGUgJ2JvZHknIG9mIHRoZSBmdW5jdGlvbiwgaWUgY29kZSB0aGF0IGdvZXMgYmV0d2VlbiB7fQoKLi4ud2hpY2ggY29tZXMgdG9nZXRoZXIgYXM6CgpgYGBmdW5jdGlvbl9uYW1lIDwtIGZ1bmN0aW9uKGFyZ3VtZW50cyl7IGZ1bmN0aW9uIGNvZGUgfWBgYAoKV2hhdCB5b3UgZ2V0IGJhY2sgZnJvbSB0aGUgZnVuY3Rpb24gd2lsbCBieSBkZWZhdWx0IGJlIHRoZSBsYXN0IHN0YXRlbWVudCB0aGUgZnVuY3Rpb24gZXZhbHVhdGVzLiAKCkJVVCB5b3UgY2FuIGFsc28gY2hvb3NlIHRvIHVzZToKCi0gKipgcmV0dXJuKClgKiogaW4gdGhlIG1pZGRsZSBvZiB0aGUgZnVuY3Rpb24gY29kZSB0byBzaWduYWwgdG8gbWFrZSBpdCBlYXNpZXIgdG8gdW5kZXJzdGFuZCwgZWcgaWYgeW91J3ZlIHdyaXR0ZW4gdmVyeSBsb25nIGBpZmAgc3RhdGVtZW50cy4KCi0gKipgaW52aXNpYmxlKClgKiogdG8gZW5zdXJlIHRoYXQgc29tZXRoaW5nIGRvZXNuJ3QgZ2V0IHByaW50ZWQgb3V0IGFzIGEgcmVzdWx0IG9mIHRoZSBmdW5jdGlvbiwgZWcgYSBuZXcgZGF0YWZyYW1lIHRoYXQgeW91J3JlIHVzaW5nIHRvIGNyZWF0ZSBhIHBsb3QKCioqRXhhbXBsZXMqKgoKSWYgd2Ugd2VyZSB0byBjcmVhdGUgYSBmdW5jdGlvbiB0aGF0IGFkZHMgMiB0byB0aGUgaW5wdXQsIGl0IHdvdWxkIGxvb2sgbGlrZSB0aGlzOgpgYGB7cn0KYWRkX3R3byA8LSBmdW5jdGlvbih4KXsKICB4KzIKfQpgYGAKCmFuZCB5b3UgY2FuIGFwcGx5IGl0IGxpa2UgdGhpcwpgYGB7cn0KYWRkX3R3bygyKQpgYGAKCnlvdSBjYW4gYWxzbyBhcHBseSBpdCB0byBldmVyeSBlbGVtZW50IG9mIGEgdmVjdG9yCmBgYHtyfQpiIDwtIGMoMTo1KQphZGRfdHdvKGIpCmBgYAoKSGVyZSdzIGFub3RoZXIgZXhhbXBsZS4gV2UgY2FuIGNyZWF0ZSBhIGBmaXp6YnV6emAgZnVuY3Rpb24gdGhhdCB0YWtlcyBhIHNpbmdsZSBudW1iZXIgYXMgaW5wdXQgYW5kIHJldHVybnMgImZpenoiIGlmIGl0J3MgZGl2aXNpYmxlIGJ5IHRocmVlIGFuZCAiYnV6eiIgaWYgaXQncyBkaXZpc2libGUgYnkgNSBhbmQgImZpenpidXp6IiBpZiBpdCdzIGRpdmlzaWJsZSBieSAzIGFuZCA1LgpgYGB7cn0KZml6emJ1enogPC0gZnVuY3Rpb24oeCkgewogIAogICMgc3RvcGlmbm90KCkgZnVuY3Rpb24gZW5zdXJlcyB0aGUgdHJ1dGggb2YgYW4gciBleHByZXNzaW9uCiAgc3RvcGlmbm90KGxlbmd0aCh4KSA9PSAxKQogIHN0b3BpZm5vdChpcy5udW1lcmljKHgpKQoKICAjICUlIGFuZCAmJiBhcmUgbG9naWNhbCBvcGVyYXRvcnMsIHdoZXJlICUlIGlzICdyZW1haW5kZXIgZnJvbSBkaXZpc2lvbicgYW5kICYmIGlzICdhbmQnICAKICBpZiAoISh4ICUlIDMpICYmICEoeCAlJSA1KSkgewogICAgImZpenpidXp6IgogIH0gZWxzZSBpZiAoISh4ICUlIDMpKSB7CiAgICAiZml6eiIKICB9IGVsc2UgaWYgKCEoeCAlJSA1KSkgewogICAgImJ1enoiCiAgfSBlbHNlIHsKICAgIHgKICB9Cn0KCmZpenpidXp6KDM3OCkKYGBgCgpJdCdzIHVzZWZ1bCB0byBoYXZlIHNvbWUgKipuYW1pbmcgY29udmVudGlvbnMqKiBmb3IgeW91ciBmdW5jdGlvbiBhcmd1bWVudHMuIFRoZSBmb2xsb3dpbmcgYXJlIHJlY29tbWVuZGVkOgoKLSBgeGAsIGB5YCBhbmQgYHpgIGZvciB2ZWN0b3JzCgotIGBkZmAgZm9yIGRhdGEgZnJhbWVzCgotIGBpYCBmb3IgdGhlIHJvdyBudW1iZXIgKGluZGljZXMpCgotIGBqYCBmb3IgdGhlIGNvbHVtbiBudW1iZXIgKGluZGljZXMpCgotIGBuYCBmb3IgdGhlIG51bWJlciBvZiByb3dzCgotIGBwYCBmb3IgdGhlIG51bWVyIG9mIGNvbHVtbnMKCkl0J3MgYWxzbyB1c2VmdWwgdG8gYWRkIGEgZ2VuZXJpYyBlcnJvciBtZXNzYWdlIHRvIHlvdXIgZnVuY3Rpb25zIHdpdGggKipgc3RvcGlmbm90KClgKiosIHdoaWNoIHlvdSBjYW4gdXNlIHRvIGFzc2VydCB3aGF0IFNIT1VMRCBiZSB0cnVlIChyYXRoZXIgdGhhbiBjaGVja2luZyBmb3Igd2hhdCBtaWdodCBiZSB3cm9uZykuIEZvciBleGFtcGxlOgoKYGBgCmZ1bmN0aW9uX25hbWUgPC0gZnVuY3Rpb24oeCwgeSwgbmEucm0gPSBGQUxTRSkgewogIHN0b3BpZm5vdChpcy5sb2dpY2FsKG5hLnJtKSwgbGVuZ3RoKG5hLnJtKSA9PSAxKQogIHN0b3BpZm5vdChsZW5ndGgoeCkgPT0gbGVuZ3RoKHkpKQogIAogIGZ1bmN0aW9uIGNvZGUKfQpgYGAKCiMjIyBGb3IgbG9vcHMKCi0gRnVuY3Rpb25zIGhlbHAgdG8gcmVkdWNlIGR1cGxpY2F0aW9uIG9mIHJlcGVhdGVkIHBhdHRlcm5zIG9mIGNvZGUKCi0gSXRlcmF0aW9uIGhlbHBzIHRvIHJlZHVjZSBkdXBsaWNhdGlvbiBvZiByZXBlYXRlZCBvcGVyYXRpb25zIG9uIG11bHRpcGxlIGlucHV0cywgZWcgcGVyZm9ybWluZyB0aGUgc2FtZSBvcGVyYXRpb24gb24gZGlmZmVyZW50IGNvbHVtbnMgb3IgZGlmZmVyZW50IGRhdGFzZXRzCgotIFRoZXJlIGFyZSAyIHR5cGVzIG9mIGl0ZXJhdGlvbjoKCiAgMS4gKipJbXBlcmF0aXZlIHByb2dyYW1taW5nKiosIGVnIGZvciBsb29wcyBhbmQgd2hpbGUgbG9vcHMKICAKICAyLiAqKkZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcqKiBtZWFucyBldmVuIGxlc3MgZHVwbGljYXRpb24gdGhhbiB5b3UgZ2V0IGluIGltcGVyYXRpdmUgcHJvZ3JhbW1pbmcKCioqRm9yIGxvb3BzOiB0aGUgYmFzaWNzKioKCi0gRXZlcnkgZm9yIGxvb3AgaGFzIDMgY29tcG9uZW50czoKCiAgMS4gQW4gb3V0cHV0LCBpZSAqKnNwZWNpZnkgd2hhdCB5b3Ugd2FudCB0aGUgb3V0cHV0IG9mIHlvdXIgZm9yIGxvb3AgdG8gYmUqKiwgZWcgYSB2ZWN0b3IgYW5kIGRldGFpbHMgbGlrZSBpdHMgdmVjdG9yIHR5cGU7IG9yIHlvdSBjb3VsZCBhbHNvIHNwZWNpZnkgeW91ciBvdXRwdXQgYXMgYmVpbmcgYSB0aWJibGUsIGEgZmFjdG9yIGV0Yy4KICAKICAyLiBBIHNlcXVlbmNlLCBpZSB3aGF0IHRvIGxvb3Agb3ZlciAmIGludm9sdmVzIGFzc2lnbmluZyBgaWAgKGxpa2UgJ2l0JykgdG8gYSBkaWZmZXJlbnQgdmFsdWUsIHNvIGBmb3IgaSBpbiBfX19fX2AKICAKICAzLiBUaGUgYm9keSB7fSwgaWUgdGhlIGNvZGUgdGhhdCBkb2VzIHRoZSB3b3JrLCBlYWNoIHRpbWUgd2l0aCBhIGRpZmZlcmVudCB2YWx1ZSBmb3IgYGlgCgoqKkV4YW1wbGVzKioKQW4gZXhhbXBsZSBvZiBhIHNpbXBsZSBmb3IgbG9vcCAgCmBgYHtyfQpvdXRwdXQgPC0gdmVjdG9yKCJkb3VibGUiLCBuY29sKG10Y2FycykpICMgVGhlIHZlY3RvciBzaG91bGQgYmUgb2YgdGhlIHR5cGUgJ2RvdWJsZScgYW5kIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIGVsZW1lbnRzIGFzIHRoZXJlIGFyZSBjb2x1bW5zIGluIG10Y2FycwogICAgICAgICAgICBuYW1lcyhvdXRwdXQpIDwtIG5hbWVzKG10Y2FycykgIyBUaGUgbmFtZXMgb2YgdGhlIGVsZW1lbnRzIHNob3VsZCBiZSB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgaW4gbXRjYXJzCiAgICAgICAgICAgIGZvciAoaSBpbiBuYW1lcyhtdGNhcnMpKSB7ICMgRm9yIGV2ZXJ5IGNvbHVtbiBpbiBtdGNhcnMKICAgICAgICAgICAgICAgICAgIG91dHB1dFtpXSA8LSBtZWFuKG10Y2Fyc1tbaV1dKSAjIG1ha2UgdGhlIGkndGggZWxlbWVudCBvZiB0aGUgdmVjdG9yIG91dHB1dCB0aGUgbWVhbiBvZiB0aGUgaSd0aCBjb2x1bW4gaW4gbXRjYXJzICh3aGVyZSBpJ3RoID0gMXN0LCAybmQsIDNyZCBldGMpCiAgICAgICAgICAgIH0Kb3V0cHV0ICMgUHJpbnQgb3V0cHV0CmBgYAoKQSBzaW1pbGFyIGV4YW1wbGUgdGhhdCBkb2Vzbid0IHJlcXVpcmUgdGhlIGxpbmUgYG5hbWVzKG91dHB1dCkgPC0gbmFtZXMobXRjYXJzKWAgYnV0IGdpdmVzIHJlc3VsdHMgd2l0aG91dCBjb2x1bW4gaGVhZGluZ3Mgd291bGQgYmU6CmBgYHtyfQpvdXRwdXQgPC0gdmVjdG9yKCJkb3VibGUiLCBuY29sKG10Y2FycykpICMgVGhlIHZlY3RvciBzaG91bGQgYmUgb2YgdGhlIHR5cGUgJ2RvdWJsZScgYW5kIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIGVsZW1lbnRzIGFzIHRoZXJlIGFyZSBjb2x1bW5zIGluIG10Y2FycwogICAgICAgICAgICBmb3IgKGkgaW4gMTpuY29sKG10Y2FycykpIHsgIyBGb3IgZXZlcnkgY29sdW1uIGluIHRoZSBudW1iZXIgb2YgY29sdW1ucyB0aGF0IG10Y2FycyBoYXMKICAgICAgICAgICAgICAgICAgIG91dHB1dFtpXSA8LSBtZWFuKG10Y2Fyc1tbaV1dKSAjIG1ha2UgdGhlIGkndGggZWxlbWVudCBvZiB0aGUgdmVjdG9yIG91dHB1dCB0aGUgbWVhbiBvZiB0aGUgaSd0aCBjb2x1bW4gaW4gbXRjYXJzICh3aGVyZSBpJ3RoID0gMXN0LCAybmQsIDNyZCBldGMpCiAgICAgICAgICAgIH0Kb3V0cHV0ICMgUHJpbnQgb3V0cHV0CmBgYAoKKipGb3IgbG9vcHM6IHZhcmlhdGlvbnMqKgoKLSBGb3IgbG9vcHMgYXJlbid0IGxpbWl0ZWQgdG8gY3JlYXRpbmcgbmV3IG9iamVjdHM7IHlvdSBjYW4gYWxzbyB1c2UgdGhlbSB0byBjaGFuZ2UgZXhpc3Rpbmcgb25lcyBieSBqdXN0IHJlbW92aW5nIHRoZSBhc3NpZ25tZW50IGF0IHRoZSBzdGFydAoKLSBGb3IgbG9vcHMgY2FuIGxvb3Agb3ZlciBuYW1lcyBhbmQgdmFsdWVzIChub3QganVzdCBpbmRpY2VzIC8gbnVtYmVyIG9mIGNvbHVtbnMpCiAgCiAgLSBsb29wIG92ZXIgZWxlbWVudHMgd2l0aCBgZm9yICh4IGluIHhzKWAKICAKICAtIGxvb3Agb3ZlciBuYW1lcyB3aXRoIGBmb3IgKG5tIGluIG5hbWVzKHhzKSlgCgotIEZvciBsb29wcyBjYW4gaGFuZGxlIG91dHB1dHMgYW5kIHNlcXVlbmNlcyBvZiB1bmtub3duIGxlbmd0aCB3aXRoICd3aGlsZScgbG9vcHMgKGFsdGhvdWdoIHRoaXMgaXMgbW9zdCB1c2VmdWwgaW4gY29udGV4dCBvZiBzaW11bGF0aW9uLCBzbyB1bmxpa2VseSB0byBuZWVkKQoKKipFeGFtcGxlcyoqCgpIb3cgdG8gbG9hZCBhIGRpcmVjdG9yeSBmdWxsIG9mIGNzdnMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lIHVzaW5nIGEgZm9yIGxvb3AuCgoxKSBDcmVhdGUgYSB2ZWN0b3Igd2l0aCB0aGUgZmlsZW5hbWUgcGF0aHMKYGBgCmZpbGVzIDwtIGRpcigiZGF0YS8iLCBwYXR0ZXJuID0gIlxcLmNzdiQiLCBmdWxsLm5hbWVzID0gVFJVRSkKYGBgCgoyKSBVc2UgYSBmb3IgbG9vcCB0aGF0IHByZWFsbG9jYXRlcyBhIGxpc3QgdG8gdGhlIGZpbGVuYW1lcyBhbmQgbG9hZHMgdGhlbSBpbiB3aXRoIHJlYWRfY3N2KCkKYGBgCmRmIDwtIHZlY3RvcigibGlzdCIiLCBsZW5ndGgoZmlsZXMpKSAKZm9yIChmbmFtZSBpbiBzZXFfYWxvbmcoZmlsZXMpKSB7CmRmW1tpXV0gPC0gcmVhZF9jc3YoZmlsZXNbW2ldXSkKfQpgYGAKCjMpIENvbWJpbmUgdGhlbSBpbnRvIGEgc2luZ2xlIGRhdGFmcmFtZQpgYGAKZGYgPC0gYmluZF9yb3dzKGRmKQpgYGAKCiMjIyBQdXJycgoKLSAqKnB1cnJyID0gYmV0dGVyIHRoYW4gZm9yIGxvb3BzKiogYmVjYXVzZSBpdCBtYWtlcyBjb2RlIGVhc2llciB0byB3cml0ZSBhbmQgdG8gcmVhZCBieSBmb2N1c2luZyBvbiB0aGUgb3BlcmF0aW9uIGJlaW5nIHBlcmZvcm1lZCAoYW5kIG5vdCB0aGUgYm9va2tlZXBpbmcgcmVxdWlyZWQgdG8gbG9vcCBvdmVyIGV2ZXJ5IGVsZW1lbnQgYW5kIHN0b3JlIHRoZSBvdXRwdXQhKQoKLSBLZXkgaWRlYSBvZiBmdW5jdGlvbmFsIHByb2dyYW1taW5nID0gcGFzc2luZyBvbmUgZnVuY3Rpb24gdG8gYW5vdGhlciBmdW5jdGlvbgoKLSBJdCBlbGltaW5hdGVzIHRoZSBuZWVkIGZvciBtYW55IGNvbW1vbiBmb3IgbG9vcHMgYW5kIGlzIHNpbWlsYXIgdG8gdGhlIGBhcHBseSgpYCBmYW1pbHkgb2YgZnVuY3Rpb25zCgotIEtleSBpZGVhIGlzIHRvIGhhdmUgZnVuY3Rpb25zIHRoYXQgcGVyZm9ybSB0aGUgY29tbW9uIHBhdHRlcm4gb2Y6CiAgCiAgMS4gTG9vcGluZyBvdmVyIGEgdmVjdG9yCiAgCiAgMi4gRG9pbmcgc29tZXRoaW5nIHRvIGVhY2ggZWxlbWVudAogIAogIDMuIFNhdmluZyB0aGUgcmVzdWx0cyAKCi0gVGhlcmUgYXJlIGRpZmZlcmVudCBwdXJyciBmdW5jdGlvbnMgZm9yIGRpZmZlcmVudCB0eXBlcyBvZiBvdXRwdXQsIGFzIGZvbGxvd3M6CgogIC0gYG1hcCgpYCB0byBtYWtlIGEgbGlzdAogIAogIC0gYG1hcF9sZ2woKWAgdG8gbWFrZSBhIGxvZ2ljYWwgdmVjdG9yCiAgCiAgLSBgbWFwX2ludCgpYCB0byBtYWtlIGFuIGludGVnZXIgdmVjdG9yCiAgCiAgLSBgbWFwX2RibCgpYCB0byBtYWtlIGEgZG91YmxlIHZlY3RvcgogIAogIC0gYG1hcF9jaHIoKWAgdG8gbWFrZSBhIGNoYXJhY3RlciB2ZWN0b3IKICAKLSBUaGVyZSBhcmUgYWxzbyB1c2VmdWwgc2hvcnRjdXRzIHlvdSBjYW4gdXNlIHdpdGhpbiBwdXJycjoKCiAgLSBgfmAgd2lsbCByZXBsYWNlIGBmdW5jdGlvbih4KWAgd2hlbiB5b3Ugd2FudCB0byBjcmVhdGUgYW4gYW5vbnltb3VzIGZ1bmN0aW9uCiAgCiAgLSBgLmAgd2lsbCByZWZlciB0byB0aGUgY3VycmVudCBsaXN0IGVsZW1lbnQgKGEgYml0IGxpa2UgaG93IGBpYCB3b3JrcyBpbiBhIGZvciBsb29wKQogIApIZXJlJ3MgdGhlIGJhc2ljIHN0cnVjdHVyZSBmb3IgdXNpbmcgYSBtYXAgZnVuY3Rpb246CmBgYAptYXBfdmVjdG9ydHlwZShkYXRhc2V0LCBmdW5jdGlvbikKYGBgCgoqKkV4YW1wbGVzKioKQ2FsY3VsYXRlIHRoZSBtZWFuIG9mIGV2ZXJ5IGNvbHVtbiBpbiBtdGNhcnMKYGBge3J9Cm1hcF9kYmwobXRjYXJzLCBtZWFuKQpgYGAKCkNvbXB1dGUgdGhlIG51bWJlciBvZiB1bmlxdWUgdmFsdWVzIGluIGVhY2ggY29sdW1uIG9mIGlyaXMKYGBge3J9CiMgVjEgV0lUSE9VVCBTSE9SVENVVFMKbWFwX2ludChpcmlzLCBmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHgpKSkKIyBWMiBXSVRIIFNIT1JUQ1VUUwptYXBfaW50KGlyaXMsIH4gbGVuZ3RoKHVuaXF1ZSguKSkpCmBgYAoKVXNlIGEgbWFwIGZ1bmN0aW9uIHdpdGhpbiBhIG11dGF0ZSwgdXNpbmcgYSBmdW5jdGlvbiB5b3UndmUgY3JlYXRlZCwgaWUgYXBwbHlpbmcgYSBmdW5jdGlvbiB0byBldmVyeSB2YXJpYWJsZSB3aXRoaW4gYSBkYXRhc2V0CmBgYHtyfQojIEV4YW1wbGUgZGF0YXNldAphIDwtIHRpYmJsZSh4ID0gYygxOjQpLCB5ID0gYygxMDA6MTAzKSkKYQoKIyBFeGFtcGxlIGZ1bmN0aW9uICh0aGUgb2xkIGFkZF90d28gY3JlYXRlZCBlYXJsaWVyIGluIHRoZSBGdW5jdGlvbnMgY2hhcHRlciBub3RlcykKYWRkX3R3byA8LSBmdW5jdGlvbih4KXsKICB4KzIKfQoKIyBFeGFtcGxlIG1hcCBmdW5jdGlvbgpjIDwtIGEgJT4lCiAgbXV0YXRlKHogPSBtYXBfZGJsKHgsIGFkZF90d28pLAogICAgICAgICB6MiA9IG1hcCh4LCBhZGRfdHdvKSkKYwpjJHoyCmBgYAoKQXBwbHkgbXVsdGlwbGUgbWFwIGZ1bmN0aW9ucyB0byBuZXN0ZWQgdGliYmxlcywgaWUgYXBwbHlpbmcgYSBzZXQgb2YgZnVuY3Rpb25zIHRvIHNlcGFyYXRlIGRhdGFzZXRzCmBgYHtyfQojIENyZWF0ZSBmdW5jdGlvbiwgaW4gdGhpcyBjYXNlIHRvIGZpbHRlciBhIGRhdGFzZXQgYnkgUGV0YWwuTGVuZ3RoID4gNQpmaWx0ZXJfcGV0YWwgPC0gZnVuY3Rpb24oZGYpewogIGRmICU+JQogIGZpbHRlcihQZXRhbC5MZW5ndGggPiA1KQp9CgojIFR1cm4gYSBub3JtYWwgZGF0YWZyYW1lIGludG8gYSBkYXRhZnJhbWUgb2YgbmVzdGVkIHRpYmJsZXMsIGluIHRoaXMgY2FzZSB0aGUgaXJpcyBkYXRhc2V0IG5lc3RlZCBieSBzcGVjaWVzCmlyaXNfbW9kIDwtIGlyaXMgJT4lCiAgZ3JvdXBfYnkoU3BlY2llcykgJT4lCiAgbmVzdCgpCgojIENyZWF0ZSAzIG5ldyBjb2x1bW5zIGluIGFsbCBvZiB0aGUgbmVzdGVkIHRpYmJsZXMKaXJpc19tb2QgJT4lCiAgCiAgIyBVc2UgbWFwIHRvIGFwcGx5IHRoZSBuZXcgZnVuY3Rpb24gZmlsdGVyX3BldGFsIHRvIGVhY2ggb2YgdGhlIHNlcGFyYXRlIGRhdGFzZXRzIGZvciBTcGVjaWVzIHdpdGhpbiBpcmlzX21vZCAoV2UgdXNlIG1hcCgpIGJlY2F1c2UgdGhlIG91dHB1dCBmcm9tIGFwcGx5aW5nIGZpbHRlcl9wZXRhbCB0byBkYXRhIGlzIGEgbGlzdC4pCiAgbXV0YXRlKGZsX3BldGFsID0gcHVycnI6Om1hcChkYXRhLCBmaWx0ZXJfcGV0YWwpLCAKICAgICAgICAgCiAgIyBJZiB3ZSB3YW50IHRvIGFkZCB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gZWFjaCBkYXRhc2V0LCB3ZSB3b3VsZCB0aGVuIHVzZSBtYXBfZGJsKCksIGJlY2F1c2UgdGhlIG91dHB1dCBmcm9tIG5yb3cgaXMgYSBzaW5nbGUgbnVtYmVyLgogIG5fZnVsbF9kYXRhID0gcHVycnI6Om1hcF9kYmwoZGF0YSwgbnJvdyksIAogIAogICMgRGl0dG8gLSB1c2UgbWFwX2RibCgpIGZvciB0aGlzIG91dHB1dCwgd2hpY2ggaXMgdGhlIG51bWJlciBvZiByb3dzIGluIG91ciBuZXcgY29sdW1uLCBmbF9wZXRhbCwgd2hpY2ggd2UgY3JlYXRlZCBhYm92ZQogIG5fcGV0YWxfbGFyZ2UgPSBwdXJycjo6bWFwX2RibChmbF9wZXRhbCwgbnJvdykpIApgYGAKCiMjIyBJdGVyYXRlIHdpdGggZ2dwbG90Cgo+PiBBZGQgY3JlYXRpbmcgYSBmdW5jdGlvbiB0byByZXBlYXQgdGhlIHNhbWUgZ2dwbG90ICYgcHJvZ3JhbW1hdGljYWxseSB3cml0aW5nIHRpdGxlcywgY2FwdGlvbnMgZXRjIDw8CgojIyMgSXRlcmF0ZSB3aXRoIHdhbGsKCioqSXRlcmF0aXZlbHkgc2F2ZSBmaWxlcyoqCgpVc2UgKipgd2FsaygpYCoqIGFuZCBpdHMgdmFyaWFudHMgKipgd2FsazJgKiogYW5kICoqYHB3YWxrKClgKiogd2hlbiB5b3Ugd2FudCB0byBjYWxsIGEgZnVuY3Rpb24gZm9yIGl0cyBzaWRlIGVmZmVjdHMsIHJhdGhlciB0aGFuIGl0cyByZXR1cm4gdmFsdWUuCgpUaGlzIGlzIHRoZSBjYXNlIHdoZW4geW91IHdhbnQgdG8gcmVuZGVyIG91dHB1dCB0byB0aGUgc2NyZWVuIG9yIHNhdmUgYSBmaWxlIHRvIGRpc2suCgpGb3IgZXhhbXBsZSwgaWYgeW91IGhhZCBhIGxpc3Qgb2YgcGxvdHMgYW5kIGEgdmVjdG9yIG9mIGZpbGVuYW1lcywgeW91IGNhbiB1c2UgYHB3YWxrKClgIGFzIGZvbGxvd3M6CmBgYHtyfQojIENyZWF0ZSBhIGxpc3Qgb2YgbXVsdGlwbGUgcGxvdHMgdXNpbmcgdGhlIG1hcCgpIGZ1bmN0aW9uCnBsb3RzIDwtIG10Y2FycyAlPiUgCiAgICAgICAgICBzcGxpdCguJGN5bCkgJT4lIAogICAgICAgICAgbWFwKH5nZ3Bsb3QoLiwgYWVzKG1wZywgd3QpKSArIGdlb21fcG9pbnQoKSkKcGxvdHMKCiMgQ3JlYXRlIGFuIGl0ZXJhdGl2ZSBmaWxlbmFtaW5nIHN0cnVjdHVyZSBmb3IgZWFjaCBwbG90LCBpbiB0aGlzIGNhc2UgdGhlIG5hbWVzIG9mIHRoZSBwbG90cyBmb2xsb3dlZCBieSAucGRmCnBhdGhzIDwtIHN0cmluZ3I6OnN0cl9jKG5hbWVzKHBsb3RzKSwgIi5wZGYiKQoKIyBTYXZlIGVhY2ggcGxvdCB0byB0aGUgcGF0aCBuYW1lcyBkZWZpbmVkIGluICdwYXRocycgYnkgY29tYmluaW5nIHRoZSBwbG90cyBhbmQgdGhlIHBhdGhuYW1lcyBpbiBhIGxpc3QgYW5kIHNhdmluZyB0aGF0IGxpc3QKcHdhbGsobGlzdChwYXRocywgcGxvdHMpLCBnZ3NhdmUsIHBhdGggPSB0ZW1wZGlyKCkpCmBgYAoKIyBDb21tdW5pY2F0ZQoKIyMjIFJNYXJrZG93bgoKQ2hvb3NlIGEgbWFya2Rvd24gdGhlbWVzIG9uICoqW0Jvb3Rzd2F0Y2hdKGh0dHBzOi8vYm9vdHN3YXRjaC5jb20vMy8pKiosIGluY2x1ZGluZzoKCi0gY29zbW8g4q2Q77iPCgotIGpvdXJuYWwKCi0gZGFya2x5IChibGFjaykKCi0gZmxhdGx5CgotIGx1bWVuCgotIHBhcGVyCgotIHJlYWRhYmxlCgotIHNhbmRzdG9uZQoKLSBzaW1wbGV4CgotIHNsYXRlIChncmV5KQoKLSBzdXBlcmhlcm8gKGJsdWUpCgpDcmVhdGUgYSB3ZWJzaXRlIGluIFJNYXJrZG93biB3aXRoICoqW2Jvb2tkb3duXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ibG9nZG93bi9nZXQtc3RhcnRlZC5odG1sKSoqCgojIEJvb2ttYXJrcwoKLSBbUiBmb3IgZGF0YSBzY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykKCi0gW0RhdGEgdmlzdWFsaXphdGlvbjogYSBwcmFjdGljYWwgaW50cm9kdWN0aW9uXShodHRwczovL3NvY3Zpei5jby9pbmRleC5odG1sI3ByZWZhY2UpCgotIFtSIE1hcmtkb3duXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vKQoKLSBbUiBhbmQgR2l0aHViXShodHRwczovL2hhcHB5Z2l0d2l0aHIuY29tL2dpdC1jbGllbnQuaHRtbCkKCi0gW1RpZHl2ZXJzZTogZ2dwbG90XShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCMpCgojIE5vdGVzCiMjIyBGYWN0b3JzCgotIEZhY3RvcnMgYXJlIHVzZWQgZm9yICoqY2F0ZWdvcmllcyoqCgotIEZhY3RvcnMgY2FuIGVpdGhlciBoYXZlIGFuIG9yZGVyIHRoYXQncyAiYXJiaXRyYXJ5IiAoZWcgaGFpciBjb2xvdXIpIG9yICJwcmluY2lwbGVkIiAoZWcgbW9udGhzKQoKLSBUaGV5J3JlIGVzcCB1c2VmdWwgaWYgeW91IHdhbnQgdG8gZGlzcGxheSBjaGFyYWN0ZXIgdmVjdG9ycyBpbiBub24tYWxwaGFiZXRpY2FsIG9yZGVyCgotIFVzZSAqKmZvcmNhdHMqKiBwYWNrYWdlIChub3QgcGFydCBvZiB0aWR5dmVyc2UpIGZvciB3b3JraW5nIHdpdGggZmFjdG9ycwoKRm9yIGV4YW1wbGUsIHNheSB5b3UgaGF2ZSBhIHN0cmluZyBvZiBtb250aHMKYGBge3J9CmxpYnJhcnkoZm9yY2F0cykKeDEgPC0gYygiRGVjIiwgIkFwciIsICJKYW4iLCAiTWFyIikKYGBgCgpUbyBjcmVhdGUgYSAiZmFjdG9yIiBvZiBtb250aHMgb3ZlciBhIHllYXI6CgogIDEuIEZpcnN0IGNyZWF0ZSBhIGxpc3Qgb2YgdGhlIGRpZmZlcmVudCBtb250aHMsIG9yICoqbGV2ZWxzKiosIGluIHRoZSBvcmRlciB0aGV5IHNob3VsZCBhcHBlYXIKYGBge3J9Cm1vbnRoX2xldmVscyA8LSBjKCJKYW4iLCAiRmViIiwgIk1hciIsICJBcHIiLCAiTWF5IiwgIkp1biIsCiAgICAgICAgICAgICAgICAgICJKdWwiLCAiQXVnIiwgIlNlcCIsICJPY3QiLCAiTm92IiwgIkRlYyIpCmBgYAoKICAyLiBUaGVuIGNyZWF0ZSBhIGZhY3RvciB3aXRoIHRoZSBmdW5jdGlvbiBmYWN0b3IoKSBhbmQgbGV2ZWxzID0gCmBgYHtyfQp5MSA8LSBmYWN0b3IoeDEsIGxldmVscyA9IG1vbnRoX2xldmVscykKYGBgCgpZb3Ugbm93IGhhdmUgYSBmYWN0b3IgdGhhdCB5b3UgY2FuICoqc29ydCoqIGFjY29yZGluZyB0byB0aGUgb3JkZXIgeW91IHNldCB3aXRoIHNvcnQoKQpgYGB7cn0Kc29ydCh5MSkKYGBgCgpZb3UgY2FuICoqY2hlY2sqKiB0aGUgbGV2ZWxzIGluIHlvdXIgZmFjdG9yIHdpdGggbGV2ZWxzKCkKYGBge3J9CmxldmVscyh5MSkKYGBgCgpZb3UgY2FuICoqcmVvcmRlcioqIHlvdXIgZmFjdG9yIGluIGF0IGxlYXN0IDIgd2F5czoKCiAgMS4gQnkgYW5vdGhlciB2YXJpYWJsZSwgZWcgd2hpY2ggbW9udGggaGFzIHRoZSBtb3N0IGJpcnRoZGF5cywgd2l0aCAqKmZjdF9yZW9yZGVyKCkqKjoKYGBge3J9CmZjdF9yZW9yZGVyKGZhY3RvciwgdmFyaWFibGUpCmBgYAogIAogIDIuIEJ5IG1vdmluZyBvbmUgb3IgbW9yZSBsZXZlbHMgdG8gdGhlIGVuZCwgZWcgc2hvd2luZyAiTm90IGFwcGxpY2FibGUiIGF0IHRoZSBlbmQsIHdpdGggKipmY3RfcmVsZXZlbCgpKio6CmBgYHtyfQpmY3RfcmVsZXZlbChmYWN0b3IsICJsZXZlbCB0byBwdWxsIG91dCIsICJsZXZlbCB0byBwdWxsIG91dCIpCmBgYAoKWW91IGNhbiBhbHNvICoqZWRpdCoqIHlvdXIgZmFjdG9yIHdpdGggKipmY3RfcmVjb2RlKiouIFRoaXMgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3IgZWRpdGluZyBsYWJlbHMgZm9yIHB1YmxpY2F0aW9uLiBBbmQgeW91IGNhbiB1c2UgdGhpcyB0ZWNobmlxdWUgdG8gY29tYmluZSBtdWx0aXBsZSAib2xkIiBsZXZlbHMgaW50byB0aGUgc2FtZSAibmV3IiBsZXZlbCwgZWcgZ3JvdXBpbmcgc2V2ZXJhbCBsZXZlbHMgdW5kZXIgIk90aGVyIi4KCmBgYHtyfQpkYXRhICU+JSBtdXRhdGUodmFyaWFibGUgPSBmY3RfcmVjb2RlKHZhcmlhYmxlLCAibmV3IG5hbWUiID0gIm9sZCBuYW1lIiwgIm5ldyBuYW1lIiA9ICJvbGQgbmFtZSIpKQpgYGAKCk90aGVyIG1vZGlmaWNhdGlvbnMgeW91IGNhbiBtYWtlIHRvIHlvdXIgZmFjdG9yIGluY2x1ZGU6CgotICoqZmN0X2NvbGxhcHNlKCkqKiwgd2hpY2ggaXMgbGlrZSBmY3RfcmVjb2RlKCksIGJ1dCB3b3JrcyBzbyB0aGF0IGZvciBlYWNoIG5ldyB2YXJpYWJsZSB5b3UgY2FuIHByb3ZpZGUgYSB2ZWN0b3Igb2Ygb2xkIGxldmVscywgZWcKYGBge3J9CmRhdGEgJT4lIG11dGF0ZSh2YXJpYWJsZSA9IGZjdF9jb2xsYXBzZSh2YXJpYWJsZSwgIm5ldyBuYW1lIiA9IGMoIm9sZCBuYW1lIiwgIm9sZCBuYW1lIiwgIm9sZCBuYW1lIikpCmBgYAoKLSAqKmZjdF9sdW1wKiosIHdoaWNoIGNydWRlbHkgbHVtcHMgdG9nZXRoZXIgYWxsIHRoZSAic21hbGwiIGdyb3VwcyB0byBzaW1wbGlmeSBhIHBsb3Qgb3IgdGFibGUKYGBge3J9CmRhdGEgJT4lIG11dGF0ZSh2YXJpYWJsZSA9IGZjdF9sdW1wKHZhcmlhYmxlKSkKYGBgCgojIyMgVmVjdG9ycwoKLSBWZWN0b3JzIGFyZSBlc3NlbnRpYWxseSB2YXJpYWJsZXMgd2l0aGluIGEgdGliYmxlCgotIFRoZXJlIGFyZSAzIHR5cGVzIG9mIHZlY3RvcjoKICAKICAxLiAqKkF0b21pYyoqIHdoaWNoIGNhbiBiZSBmdXJ0aGVyIHN1YmNhdGVnb3Jpc2VkIGludG86CiAgCiAgICAgIC0gKipsb2dpY2FsKiogKFRSVUUsIEZBTFNFIGFuZCBOQSkKICAgICAgCiAgICAgIC0gKippbnRlZ2VyKiogKG51bWVyaWMgd2l0aG91dCBkZWNpbWFsIHBsYWNlcywgZWcgMjQzKQogICAgICAKICAgICAgLSAqKmRvdWJsZSoqIChudW1lcmljIHdpdGggZGVjaW1hbCBwbGFjZXMsIGVnIDI0My41Njc4KSAKICAgICAgCiAgICAgIC0gKipjaGFyYWN0ZXIqKiAobWFkZSB1cCBvZiBzdHJpbmdzKQogICAgICAKICAgICAgLSAqKmNvbXBsZXgqKiAocmFyZWx5IHVzZWQgZHVyaW5nIGRhdGEgYW5hbHlzaXMpCiAgICAgIAogICAgICAtICoqcmF3KiogKGRpdHRvKQogIAogIDIuICoqTGlzdHMqKiAoYWthICdyZWN1cnNpdmUgdmVjdG9ycycgYW5kIGNhbiBjb250YWluIG90aGVyIGxpc3RzLCBzbyB0aGV5J3JlIGdvb2QgZm9yIHJlcHJlc2VudGluZyBoaWVyYXJjaGllcyBpbiBkYXRhKQoKICAzLiAqKmBOVUxMYCoqIHdoaWNoIGlzIHRoZSBhYnNlbmNlIG9mIGEgdmVjdG9yIChhbmQgZGlzdGluY3QgZnJvbSBgTkFgIHdoaWNoIGlzIHRoZSBhYnNlbmNlIG9mIGEgdmFsdWUgaW4gYSB2ZWN0b3IpCgoqKk5vdGUgYWJvdXQgaW50ZWdlcnMgdnMgZG91YmxlcyoqOiBpZiB5b3UganVzdCBlbnRlciBgMmAgaW4gUiwgaXQgaW50ZXJwcmV0cyB0aGlzIGFzIGAyLjAwMDAwMGAsIGllIGEgZG91YmxlLiBUbyBlbnRlciBgMmAgYXMgYW4gaW50ZWdlciB5b3UgaGF2ZSB0byB3cml0ZSBgMkxgLiAKYGBge3J9CnR5cGVvZigyKQp0eXBlb2YoMkwpCnR5cGVvZigyLjVMKQpgYGAKClNvIHdoYXQgY2FuIHlvdSBkbyB3aXRoIHZlY3RvcnM/CgotIFlvdSBjYW4gZmluZCBvdXQgd2hhdCB0eXBlIG9mIHZlY3RvciBhIHZlY3RvciBpcyB3aXRoICoqYHR5cGVvZigpKiogCgotIFlvdSBjYW4gZmluZCBvdXQgaG93IG1hbnkgaXRlbXMgdGhlcmUgYXJlIGluIGEgdmVjdG9yIHdpdGggKipgbGVuZ3RoKCkqKgoKLSBZb3UgY2FuIHNwZWNpZnkgaG93IHRvIHJlYWQgYSB2ZWN0b3Igd2hlbiB5b3UgaW1wb3J0IHdpdGggYHJlYWRyYCBieSB1c2luZyBgY29sX3R5cGVzYCBzcGVjaWZpY2F0aW9ucywgZm9yIGV4YW1wbGU6CgpgYGB7cn0KZGF0YSA8LSByZWFkX2NzdigiZGF0YS5jc3YiKSwgCiAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scyhjb2x1bW5fbmFtZSA9IGNvbF9sb2dpY2FsKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF9pbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF9kb3VibGUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lID0gY29sX2NoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWUgPSBjb2xfZGF0ZShmb3JtYXQgPSAiIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF90aW1lKGZvcm1hdCA9ICIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lID0gY29sX2RhdGV0aW1lKGZvcm1hdCA9ICIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lID0gY29sX251bWJlcigpLCAjIElzbid0IGZ1c3N5IGFib3V0IG51bWJlcnMgY29udGFpbmluZyBjb21tYXMsICQgZXRjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF9za2lwKCksICMgU2tpcHMgaW1wb3J0aW5nIHRoaXMgY29sdW1uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZSA9IGNvbF9ndWVzcygpICMgR3Vlc3NlcyBob3cgdG8gcGFyc2UgYmFzZWQgb24gaW5wdXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKYGBgCgotIFlvdSBjYW4gY29udmVydCBvbmUgdHlwZSBvZiB2ZWN0b3IgdG8gYW5vdGhlciB3aXRoICoqYGFzLmxvZ2ljYWwoKWAqKiwgKipgYXMuaW50ZWdlcigpYCoqLCAqKmBhcy5kb3VibGUoKWAqKiBvciAqKmBhcy5jaGFyYWN0ZXIoKWAqKgoKLSBZb3UgY2FuIHBlcmZvcm0gYmFzaWMgbWF0aHMgb3BlcmF0aW9ucyBvbiB2ZWN0b3JzLCBpZSBkb24ndCBuZWVkIHRvIGl0ZXJhdGUgZm9yIGV2ZXJ5IGl0ZW0gaW4gdGhlIHZlY3RvcgoKLSBZb3UgY2FuIGNyZWF0ZSBhbmQgbmFtZSBhIHZlY3RvciB3aXRoIGBjKClgCgoqKkxpc3RzKioKCi0gQ2FuIGNvbnRhaW4gYSBtaXggb2YgYXRvbWljIHZlY3RvciB0eXBlcyBhbmQgb3RoZXIgbGlzdHMKCi0gSXQncyB1c2VmdWwgdG8gdXNlIGBzdHIoKWAgYmVjYXVzZSB0aGlzIHdpbGwgZ2l2ZSB5b3UgdGhlIHN0cnVjdHVyZSBvZiB5b3VyIGxpc3QsIHJhdGhlciB0aGFuIHRoZSBjb250ZW50cwoKLSBUbyBzdWJzZXQgaXRlbXMgd2l0aGluIGEgbGlzdCB1c2UgZG91YmxlIGJyYWNrZXRzIGBbW11dYCByYXRoZXIgdGhhbiBzaW5nbGUgYFtdYC4KCioqQXVnbWVudGVkIHZlY3RvcnMqKgoKLSBGYWN0b3JzLCBkYXRlcywgZGF0ZS10aW1lcyBhbmQgdGliYmxlcyBhcmUgYXVnbWVudGVkIHZlY3RvcnMsIGJlY2F1c2UgdGhleSBoYXZlIGFkZGl0aW9uYWwgYXR0cmlidXRlcyAob24gdG9wIG9mIHRoZSB1c3VhbCB2ZWN0b3IgYXR0cmlidXRlcyBvZiBuYW1lcywgZGltZW5zaW9ucyBhbmQgY2xhc3MpCgogIC0gRmFjdG9ycyBoYXZlIGEgJ2xldmVscycgYXR0cmlidXRlCiAgCiAgLSBEYXRlcyBhbmQgZGF0ZS10aW1lcyBoYXZlIGFkZGl0aW9uYWwgJ2NsYXNzJyBhdHRyaWJ1dGVzCiAgCiAgLSBUaWJibGVzIGhhdmUgYWRkaXRpb25hbCAnY2xhc3MnIGF0dHJpYnV0ZXMgKGB0YmxfZGZgLCBgdGJsYCBhbmQgYGRhdGEuZnJhbWVgKSBhcyB3ZWxsIGFzIGNvbHVtbiBgbmFtZXNgIGFuZCBgcm93Lm5hbWVzYAo=