Core Concepts and the Library Metaphor

To understand R, you must first know the difference between a package and a library.

An R package is a shareable toolkit that bundles together useful functions, data, and user guides. An R library is simply the folder on your computer where these toolkits are stored. Think of an R package as a specific book, while the R library is the physical bookshelf holding all your books.

When you work with R, you search your library shelves to find the exact package toolkit you need for your data analysis task.

Inside an R Package

Every installed package follows a strict folder structure behind the scenes;

  • The heart of the package is the R/ folder, which contains the actual code and programming functions.

  • The man/ folder holds the instruction manuals, which pop up on your screen whenever you ask R for help with a function.

You will also find a DESCRIPTION file containing the package name, author, and version details, along with a NAMESPACE file that controls how the package interacts with the rest of R. Some packages even include a data/ folder filled with sample datasets for practice.

Where Packages Live

Before you can use a package, you must download it from an online storage repository. The most important one is CRAN, the Comprehensive R Archive Network.

CRAN is the official marketplace for R packages, hosting tens of thousands of options that must pass strict quality checks. For specialized fields like biology and genetics, scientists use a dedicated repository called Bioconductor.

Finally, developers often share early, experimental versions of their toolkits on open platforms like GitHub before official release.

Managing Your Toolkits

You interact with your R library using a few basic commands;

  • To download a new toolkit from CRAN, you use install.packages(). Once it is on your computer, you must run library() at the start of your script to open that toolkit and make its tools active for your current session.

If you want to see exactly where your library folder is hidden on your computer, type .libPaths(). You can also run installed.packages() to see a complete checklist of everything you have installed, or update.packages() to ensure all your tools are running on the latest versions.

Installing & Loading Libraries

Now load them.

Defining Packages

1. tidyverse Contains

The tidyverse provides almost everything needed for modern data cleaning and manipulation. It contains;

  • dplyr, tidyr, ggplot2, readr, tibble, purrr, forcats, stringr

This is the backbone of modern R.

2. here

One of the most important packages. It makes your project reproducible on any computer.

When sharing R scripts, people usually hardcode the file_paths to files on their specific computer or use setwd("C:/MyFolder"). When another person/student or a teacher tries to run that code on a different computer, the script breaks instantly because that exact file path does not exist.

The Old, Broken Way (Avoid This)

If you move this file to another computer, it crashes immediately

# This will break on your students' or grading computers

# setwd("C:/Users/Ernest/Session1/R_Guide")
# 
# df <- read_xlsx("Data/Data1_Session1.xlsx")

The modern way

This code works perfectly on Mac, Windows, Linux, cloud servers, and any student’s machine. The 1st entry “Data” - is the folder where the data is stored, and the 2nd entry “Data1_Session1.xlsx” - is the exact data we are interested in.


library(here)
library(readr)

# This automatically finds the project root and builds the correct path

df <- read_xlsx(here("Data", "Data1_Session1.xlsx"), sheet = "Sheet2")

print(dim(df))
[1] 80  6
print(names(df))
[1] "household_ID"   "region"         "water source"   "household size" "age of hh-head"
[6] "date_of_birth" 

here: Does not import data itself, but safely calculates the exact folder path to your files so the other packages can open them without breaking.

3. readxl, haven

To bring external files into R, we rely on a specialized team of packages. Together, they allow us to import almost any common data format you will encounter:

  • readr (via tidyverse): Built for reading flat text files like CSV (comma-separated), TSV (tab-separated), and plain text logs.

  • readxl: Specialized for extracting tabular data directly out of Microsoft Excel spreadsheets (.xls and .xlsx).

  • haven: Built to read and write data files generated by other major statistical software, specifically SPSS (.sav), Stata (.dta), and SAS (.sas7bdat).


library(readxl)
library(haven)

# CSV
df_csv <- read_csv(
  here("Data", "df_Session1_edited.csv")
)

# Excel
df_excel <- read_excel(
  here("Data", "df_Session1_edited.xlsx")
)

# SPSS
df_spss <- read_sav(
  here("Data", "df_Session1_edited.sav")
)

# Stata
df_stata <- read_dta(
  here("Data", "df_Session1_edited.dta")
)

4. janitor

Used for;

  • Cleaning variable names

  • Removing duplicate rows

  • Creating frequency tables

For example, instead of manually renaming columns like “water source” to “water_source”, you use the command below to achieve the same task for all other columns.

df <- df %>%
  clean_names()

print(names(df))
[1] "household_id"   "region"         "water_source"   "household_size" "age_of_hh_head"
[6] "date_of_birth" 

5. skimr

Produces an extremely detailed summary comprising of the following;

1. Data Frame Overview (Metadata)

  • Dimensions: Total number of rows and columns.

  • Data Types: A strict count of how many columns are numeric, text, dates, or factors.

  • Grouping: Indicates if the data frame is currently grouped (e.g., via group_by()).

2. Information for Every Data Type

  • Missingness: The exact number of missing values (n_missing) and the complete rate percentage (complete_rate).

  • Data Footprint: Total number of unique values inside a column.

3. Deep Dive for Text / Character Columns

  • Length Stats: The minimum, maximum, and average character length of strings in that column.

  • Whitespace: Counts of empty strings or hidden spaces.

4. Detailed Metrics for Numeric Columns

  • Central Tendency: Mean (average) and standard deviation.

  • Percentiles & Distribution: The complete 5-number summary (minimum, 25th percentile, median, 75th percentile, and maximum).

  • The In-line Histogram: A tiny, text-based visual sparkline graph showing the distribution shape of your numbers directly inside your R console.

Instead of producing this below;

summary(df)
    household_id       region      water_source household_size  age_of_hh_head 
 Length   :80    Length   :80   Length   :80    Min.   :1.000   Min.   :20.00  
 N.unique :80    N.unique : 4   N.unique : 4    1st Qu.:3.000   1st Qu.:32.00  
 N.blank  : 0    N.blank  : 0   N.blank  : 0    Median :4.000   Median :46.00  
 Min.nchar: 7    Min.nchar: 7   Min.nchar: 5    Mean   :4.039   Mean   :47.88  
 Max.nchar: 7    Max.nchar: 8   Max.nchar:14    3rd Qu.:5.000   3rd Qu.:60.00  
                 NAs      : 1   NAs      : 5    Max.   :9.000   Max.   :80.00  
                                                NAs    :3                      
 date_of_birth                
 Min.   :1947-03-04 00:00:00  
 1st Qu.:1967-04-11 12:00:00  
 Median :1981-01-09 00:00:00  
 Mean   :1979-03-24 14:24:00  
 3rd Qu.:1995-03-18 06:00:00  
 Max.   :2007-05-25 00:00:00  
                              

you simply do

skim(df)
── Data Summary ────────────────────────
                           Values
Name                       df    
Number of rows             80    
Number of columns          6     
_______________________          
Column type frequency:           
  character                3     
  numeric                  2     
  POSIXct                  1     
________________________         
Group variables            None  

6. naniar

Example;

Option 1: missingness heatmap (or a missing data heatmap)

Professional package for handling missing data. This produces an excellent missing data visualization. It acts as a visual X-ray of your spreadsheet to show exactly where missing values (NAs) are located.

#library(visdat)

vis_miss(df)

Here is how to interpret it step-by-step:

1. The Grid System (Rows and Columns)

  • Columns (Top Axis): Each vertical section represents a column in your data frame (household_id, region, water_source, household_size, age_of_hh_head, and date_of_birth)

  • Rows (Left Axis): The vertical axis represents your 80 individual rows (observations), starting at row 1 at the top and going down to row 80 at the bottom.

2. The Color Meaning

  • Grey blocks (98.1%): This represents data that is present and filled in. Your dataset is almost entirely complete.

  • Black lines (1.9%): This represents missing values (NA).

3. Reading the “Data Story” in this Graph

Instead of just telling you how many values are missing, this graph tells you where they are missing:

  • Look at the tiny black horizontal lines on the graph.

  • Those lines show that a couple of households in your dataset have missing data specifically in the region, water_source and household_size columns.

Option 2: Switch to a Bar Chart (The Best Classroom Alternative)

If you are just interested to see the total count of missing values clearly without worrying about their spread in rows, you switch from vis_miss() to gg_miss_var() still under the naniar package. This creates a clean bar chart that clearly labels how many NAs are in every single column

# Creates a clear dot/bar plot showing the exact number of NAs per column
gg_miss_var(df)

7. lubridate

Makes dates easy. Instead of complicated date conversions like as.Date();

you can simply do this;

For ymd()

df <- df %>%
  mutate(
    ymd_format = ymd(date_of_birth)
  )

print(head(df[, c("date_of_birth", "ymd_format")], 5))
# A tibble: 5 × 2
  date_of_birth       ymd_format
  <dttm>              <date>    
1 1979-07-28 00:00:00 1979-07-28
2 1995-07-16 00:00:00 1995-07-16
3 1959-03-27 00:00:00 1959-03-27
4 1996-06-26 00:00:00 1996-06-26
5 2007-05-25 00:00:00 2007-05-25

For dmy() or mdy()

df <- df %>%
  mutate(
    dob_dmy = format(date_of_birth, "%d-%m-%Y"),
    dmy_format = dmy(dob_dmy)
  )

print(head(df[, c("date_of_birth", "dmy_format")], 5))
# A tibble: 5 × 2
  date_of_birth       dmy_format
  <dttm>              <date>    
1 1979-07-28 00:00:00 1979-07-28
2 1995-07-16 00:00:00 1995-07-16
3 1959-03-27 00:00:00 1959-03-27
4 1996-06-26 00:00:00 1996-06-26
5 2007-05-25 00:00:00 2007-05-25

8. ggplot2

The ggplot2 package is the premier data visualization toolkit for R, built to create professional, publication-quality graphs. It is automatically loaded whenever students run library(tidyverse). 1]

The “gg” in ggplot2 stands for the Grammar of Graphics. This is a powerful, structured framework that allows users to build graphs by stacking distinct, independent layers on top of each other, rather than trying to build a plot all at once. 1, 2, 3, 4]

The 3 Core Components

To make any graph in ggplot2, students only need to understand three fundamental components. You can present this formula in your notes: [1]

  • Data + Aesthetics (Mapping) + Geometries (Layers) = A Graph

1. The Data

This is the raw data frame containing the variables you want to plot (e.g., your df containing household sizes and regions). [1]

2. The Aesthetics (aes)

Aesthetics tell R how to connect variables in your data to visual properties on the screen. This answers questions like: [1]

  • What variable goes on the X-axis?

  • What variable goes on the Y-axis?

  • Should the data points be colored or sized based on a specific category? [1]

3. The Geometries (geom)

Geometries define the actual shapes that appear on the canvas to represent the data. [1, 2]

  • Use geom_point() to make a scatter plot.

  • Use geom_bar() or geom_col() to make bar charts.

  • Use geom_boxplot() to make a box-and-whisker plot.

library(tidyverse)

# 1. Start with your data frame
ggplot(data = df, mapping = aes(x = region, y = household_size, fill = region)) + 
  
  # 2. Draw the boxes and make the line widths a bit cleaner
  geom_boxplot(alpha = 0.8, color = "black") + 
  
  # 3. Apply a vibrant, professional color palette (e.g., ColorBrewer "Set2")
  scale_fill_brewer(palette = "Set2") +
  
  # 4. Strip away the heavy grey background for a clean look
  theme_minimal() + 
  
  # 5. Customize text and fix the tilted/cut-off labels on the X-axis
  theme(
    legend.position = "none",  # Hides the redundant legend since X-axis is labeled
    axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, face = "bold"),
    plot.title = element_text(face = "bold", size = 14)
  ) +
  
  # 6. Add complete labels
  labs(
    title = "Distribution of Household Size by Region", 
    x = "Geographic Region", 
    y = "Number of People per Household"
  )

9. Saving data

Saving the data on to your laptop in any format

library(here)
library(writexl)
library(readr)
library(haven)

# Excel
write_xlsx(
  df,
  here("Data", "df_Session1_edited.xlsx")
)

# CSV
write_csv(
  df,
  here("Data", "df_Session1_edited.csv")
)

# SPSS
write_sav(
  df,
  here("Data", "df_Session1_edited.sav")
)

# Stata
write_dta(
  df,
  here("Data", "df_Session1_edited.dta")
)

<— End of Session —>

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2s6IFVuZGVyc3RhbmRpbmcgWW91ciBSIExpYnJhcmllcyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgDQoNCiMjICoqQ29yZSBDb25jZXB0cyBhbmQgdGhlIExpYnJhcnkgTWV0YXBob3IqKg0KDQpUbyB1bmRlcnN0YW5kIFIsIHlvdSBtdXN0IGZpcnN0IGtub3cgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIHBhY2thZ2UgYW5kIGEgbGlicmFyeS4NCg0KQW4gKipSIHBhY2thZ2UqKiBpcyBhIHNoYXJlYWJsZSB0b29sa2l0IHRoYXQgYnVuZGxlcyB0b2dldGhlciB1c2VmdWwgZnVuY3Rpb25zLCBkYXRhLCBhbmQgdXNlciBndWlkZXMuIEFuICoqUiBsaWJyYXJ5KiogaXMgc2ltcGx5IHRoZSBmb2xkZXIgb24geW91ciBjb21wdXRlciB3aGVyZSB0aGVzZSB0b29sa2l0cyBhcmUgc3RvcmVkLiBUaGluayBvZiBhbiBSIHBhY2thZ2UgYXMgYSBzcGVjaWZpYyBib29rLCB3aGlsZSB0aGUgUiBsaWJyYXJ5IGlzIHRoZSBwaHlzaWNhbCBib29rc2hlbGYgaG9sZGluZyBhbGwgeW91ciBib29rcy4NCg0KV2hlbiB5b3Ugd29yayB3aXRoIFIsIHlvdSBzZWFyY2ggeW91ciBsaWJyYXJ5IHNoZWx2ZXMgdG8gZmluZCB0aGUgZXhhY3QgcGFja2FnZSB0b29sa2l0IHlvdSBuZWVkIGZvciB5b3VyIGRhdGEgYW5hbHlzaXMgdGFzay4NCg0KIyMgKipJbnNpZGUgYW4gUiBQYWNrYWdlKioNCg0KRXZlcnkgaW5zdGFsbGVkIHBhY2thZ2UgZm9sbG93cyBhIHN0cmljdCBmb2xkZXIgc3RydWN0dXJlIGJlaGluZCB0aGUgc2NlbmVzOw0KDQotIFRoZSBoZWFydCBvZiB0aGUgcGFja2FnZSBpcyB0aGUgYFIvYCBmb2xkZXIsIHdoaWNoIGNvbnRhaW5zIHRoZSBhY3R1YWwgY29kZSBhbmQgcHJvZ3JhbW1pbmcgZnVuY3Rpb25zLg0KDQotIFRoZSBgbWFuL2AgZm9sZGVyIGhvbGRzIHRoZSBpbnN0cnVjdGlvbiBtYW51YWxzLCB3aGljaCBwb3AgdXAgb24geW91ciBzY3JlZW4gd2hlbmV2ZXIgeW91IGFzayBSIGZvciBoZWxwIHdpdGggYSBmdW5jdGlvbi4NCg0KWW91IHdpbGwgYWxzbyBmaW5kIGEgYERFU0NSSVBUSU9OYCBmaWxlIGNvbnRhaW5pbmcgdGhlIHBhY2thZ2UgbmFtZSwgYXV0aG9yLCBhbmQgdmVyc2lvbiBkZXRhaWxzLCBhbG9uZyB3aXRoIGEgYE5BTUVTUEFDRWAgZmlsZSB0aGF0IGNvbnRyb2xzIGhvdyB0aGUgcGFja2FnZSBpbnRlcmFjdHMgd2l0aCB0aGUgcmVzdCBvZiBSLiBTb21lIHBhY2thZ2VzIGV2ZW4gaW5jbHVkZSBhIGBkYXRhL2AgZm9sZGVyIGZpbGxlZCB3aXRoIHNhbXBsZSBkYXRhc2V0cyBmb3IgcHJhY3RpY2UuDQoNCiMjICoqV2hlcmUgUGFja2FnZXMgTGl2ZSoqDQoNCkJlZm9yZSB5b3UgY2FuIHVzZSBhIHBhY2thZ2UsIHlvdSBtdXN0IGRvd25sb2FkIGl0IGZyb20gYW4gb25saW5lIHN0b3JhZ2UgcmVwb3NpdG9yeS4gVGhlIG1vc3QgaW1wb3J0YW50IG9uZSBpcyAqKkNSQU4qKiwgdGhlIENvbXByZWhlbnNpdmUgUiBBcmNoaXZlIE5ldHdvcmsuDQoNCkNSQU4gaXMgdGhlIG9mZmljaWFsIG1hcmtldHBsYWNlIGZvciBSIHBhY2thZ2VzLCBob3N0aW5nIHRlbnMgb2YgdGhvdXNhbmRzIG9mIG9wdGlvbnMgdGhhdCBtdXN0IHBhc3Mgc3RyaWN0IHF1YWxpdHkgY2hlY2tzLiBGb3Igc3BlY2lhbGl6ZWQgZmllbGRzIGxpa2UgYmlvbG9neSBhbmQgZ2VuZXRpY3MsIHNjaWVudGlzdHMgdXNlIGEgZGVkaWNhdGVkIHJlcG9zaXRvcnkgY2FsbGVkICoqQmlvY29uZHVjdG9yKiouDQoNCkZpbmFsbHksIGRldmVsb3BlcnMgb2Z0ZW4gc2hhcmUgZWFybHksIGV4cGVyaW1lbnRhbCB2ZXJzaW9ucyBvZiB0aGVpciB0b29sa2l0cyBvbiBvcGVuIHBsYXRmb3JtcyBsaWtlICoqR2l0SHViKiogYmVmb3JlIG9mZmljaWFsIHJlbGVhc2UuDQoNCiMjICoqTWFuYWdpbmcgWW91ciBUb29sa2l0cyoqDQoNCllvdSBpbnRlcmFjdCB3aXRoIHlvdXIgUiBsaWJyYXJ5IHVzaW5nIGEgZmV3IGJhc2ljIGNvbW1hbmRzOw0KDQotIFRvIGRvd25sb2FkIGEgbmV3IHRvb2xraXQgZnJvbSBDUkFOLCB5b3UgdXNlIGBpbnN0YWxsLnBhY2thZ2VzKClgLiBPbmNlIGl0IGlzIG9uIHlvdXIgY29tcHV0ZXIsIHlvdSBtdXN0IHJ1biBgbGlicmFyeSgpYCBhdCB0aGUgc3RhcnQgb2YgeW91ciBzY3JpcHQgdG8gb3BlbiB0aGF0IHRvb2xraXQgYW5kIG1ha2UgaXRzIHRvb2xzIGFjdGl2ZSBmb3IgeW91ciBjdXJyZW50IHNlc3Npb24uDQoNCklmIHlvdSB3YW50IHRvIHNlZSBleGFjdGx5IHdoZXJlIHlvdXIgbGlicmFyeSBmb2xkZXIgaXMgaGlkZGVuIG9uIHlvdXIgY29tcHV0ZXIsIHR5cGUgYC5saWJQYXRocygpYC4gWW91IGNhbiBhbHNvIHJ1biBgaW5zdGFsbGVkLnBhY2thZ2VzKClgIHRvIHNlZSBhIGNvbXBsZXRlIGNoZWNrbGlzdCBvZiBldmVyeXRoaW5nIHlvdSBoYXZlIGluc3RhbGxlZCwgb3IgYHVwZGF0ZS5wYWNrYWdlcygpYCB0byBlbnN1cmUgYWxsIHlvdXIgdG9vbHMgYXJlIHJ1bm5pbmcgb24gdGhlIGxhdGVzdCB2ZXJzaW9ucy4NCg0KIyMgKipJbnN0YWxsaW5nICYgTG9hZGluZyBMaWJyYXJpZXMqKg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgSW5zdGFsbCBwYWNrYWdlcyAoUnVuIG9ubHkgb25jZSkNCg0KIyBpbnN0YWxsLnBhY2thZ2VzKGMoDQojICAgInRpZHl2ZXJzZSIsDQojICAgInJlYWR4bCIsDQojICAgImphbml0b3IiLA0KIyAgICJza2ltciIsDQojICAgIm5hbmlhciIsDQojICAgImx1YnJpZGF0ZSIsDQojICAgInN0cmluZ3IiLA0KIyAgICJoZXJlIiwNCiMgICAid3JpdGV4bCIsDQojICAgInBzeWNoIg0KIyApKQ0KDQpgYGANCg0KTm93IGxvYWQgdGhlbS4NCg0KYGBge3IsIGVjaG89RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShza2ltcikNCmxpYnJhcnkobmFuaWFyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KHdyaXRleGwpDQpsaWJyYXJ5KHBzeWNoKQ0KDQpgYGANCg0KIyMgKipEZWZpbmluZyBQYWNrYWdlcyoqDQoNCiMjICoqKjEuIHRpZHl2ZXJzZSoqKiAqKkNvbnRhaW5zKioNCg0KVGhlIHRpZHl2ZXJzZSBwcm92aWRlcyBhbG1vc3QgZXZlcnl0aGluZyBuZWVkZWQgZm9yIG1vZGVybiBkYXRhIGNsZWFuaW5nIGFuZCBtYW5pcHVsYXRpb24uIEl0IGNvbnRhaW5zOw0KDQotIGRwbHlyLCB0aWR5ciwgZ2dwbG90MiwgcmVhZHIsIHRpYmJsZSwgcHVycnIsIGZvcmNhdHMsIHN0cmluZ3INCg0KVGhpcyBpcyB0aGUgYmFja2JvbmUgb2YgbW9kZXJuIFIuDQoNCiMjICoqKjIuIGhlcmUqKioNCg0KT25lIG9mIHRoZSBtb3N0IGltcG9ydGFudCBwYWNrYWdlcy4gSXQgbWFrZXMgeW91ciBwcm9qZWN0IHJlcHJvZHVjaWJsZSBvbiBhbnkgY29tcHV0ZXIuDQoNCldoZW4gc2hhcmluZyBSIHNjcmlwdHMsIHBlb3BsZSB1c3VhbGx5IGhhcmRjb2RlIHRoZSBmaWxlX3BhdGhzIHRvIGZpbGVzIG9uIHRoZWlyIHNwZWNpZmljIGNvbXB1dGVyIG9yIHVzZSBgc2V0d2QoIkM6L015Rm9sZGVyIilgLiBXaGVuIGFub3RoZXIgcGVyc29uL3N0dWRlbnQgb3IgYSB0ZWFjaGVyIHRyaWVzIHRvIHJ1biB0aGF0IGNvZGUgb24gYSBkaWZmZXJlbnQgY29tcHV0ZXIsIHRoZSBzY3JpcHQgYnJlYWtzIGluc3RhbnRseSBiZWNhdXNlIHRoYXQgZXhhY3QgZmlsZSBwYXRoIGRvZXMgbm90IGV4aXN0Lg0KDQojIyMgKipUaGUgT2xkLCBCcm9rZW4gV2F5IChBdm9pZCBUaGlzKSoqDQoNCklmIHlvdSBtb3ZlIHRoaXMgZmlsZSB0byBhbm90aGVyIGNvbXB1dGVyLCBpdCBjcmFzaGVzIGltbWVkaWF0ZWx5DQoNCmBgYHtyfQ0KIyBUaGlzIHdpbGwgYnJlYWsgb24geW91ciBzdHVkZW50cycgb3IgZ3JhZGluZyBjb21wdXRlcnMNCg0Kc2V0d2QoIkM6L1VzZXJzL0VybmVzdC9TZXNzaW9uMS9SX0d1aWRlIikNCg0KZGYgPC0gcmVhZF94bHN4KCJEYXRhL0RhdGExX1Nlc3Npb24xLnhsc3giKQ0KYGBgDQoNCiMjIyAqKlRoZSBtb2Rlcm4gd2F5KioNCg0KVGhpcyBjb2RlIHdvcmtzIHBlcmZlY3RseSBvbiBNYWMsIFdpbmRvd3MsIExpbnV4LCBjbG91ZCBzZXJ2ZXJzLCBhbmQgYW55IHN0dWRlbnQncyBtYWNoaW5lLiBUaGUgMXN0IGVudHJ5ICJEYXRhIiAtIGlzIHRoZSBmb2xkZXIgd2hlcmUgdGhlIGRhdGEgaXMgc3RvcmVkLCBhbmQgdGhlIDJuZCBlbnRyeSAiRGF0YTFfU2Vzc2lvbjEueGxzeCIgLSBpcyB0aGUgZXhhY3QgZGF0YSB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4NCg0KYGBge3J9DQoNCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkocmVhZHIpDQoNCiMgVGhpcyBhdXRvbWF0aWNhbGx5IGZpbmRzIHRoZSBwcm9qZWN0IHJvb3QgYW5kIGJ1aWxkcyB0aGUgY29ycmVjdCBwYXRoDQoNCmRmIDwtIHJlYWRfeGxzeChoZXJlKCJEYXRhIiwgIkRhdGExX1Nlc3Npb24xLnhsc3giKSwgc2hlZXQgPSAiU2hlZXQyIikNCg0KcHJpbnQoZGltKGRmKSkNCnByaW50KG5hbWVzKGRmKSkNCmBgYA0KDQoqKmBoZXJlYCoqOiBEb2VzIG5vdCBpbXBvcnQgZGF0YSBpdHNlbGYsIGJ1dCBzYWZlbHkgY2FsY3VsYXRlcyB0aGUgZXhhY3QgKipmb2xkZXIgcGF0aCoqIHRvIHlvdXIgZmlsZXMgc28gdGhlIG90aGVyIHBhY2thZ2VzIGNhbiBvcGVuIHRoZW0gd2l0aG91dCBicmVha2luZy4NCg0KIyMgKioqMy4gcmVhZHhsLCBoYXZlbioqKg0KDQpUbyBicmluZyBleHRlcm5hbCBmaWxlcyBpbnRvIFIsIHdlIHJlbHkgb24gYSBzcGVjaWFsaXplZCB0ZWFtIG9mIHBhY2thZ2VzLiBUb2dldGhlciwgdGhleSBhbGxvdyB1cyB0byBpbXBvcnQgYWxtb3N0IGFueSBjb21tb24gZGF0YSBmb3JtYXQgeW91IHdpbGwgZW5jb3VudGVyOg0KDQotICoqYHJlYWRyYCoqICh2aWEgKipgdGlkeXZlcnNlYCoqKTogQnVpbHQgZm9yIHJlYWRpbmcgZmxhdCB0ZXh0IGZpbGVzIGxpa2UgKipDU1YqKiAoY29tbWEtc2VwYXJhdGVkKSwgVFNWICh0YWItc2VwYXJhdGVkKSwgYW5kIHBsYWluIHRleHQgbG9ncy4NCg0KLSAqKmByZWFkeGxgKio6IFNwZWNpYWxpemVkIGZvciBleHRyYWN0aW5nIHRhYnVsYXIgZGF0YSBkaXJlY3RseSBvdXQgb2YgTWljcm9zb2Z0ICoqRXhjZWwqKiBzcHJlYWRzaGVldHMgKGAueGxzYCBhbmQgYC54bHN4YCkuDQoNCi0gKipgaGF2ZW5gKio6IEJ1aWx0IHRvIHJlYWQgYW5kIHdyaXRlIGRhdGEgZmlsZXMgZ2VuZXJhdGVkIGJ5IG90aGVyIG1ham9yIHN0YXRpc3RpY2FsIHNvZnR3YXJlLCBzcGVjaWZpY2FsbHkgKipTUFNTKiogKGAuc2F2YCksICoqU3RhdGEqKiAoYC5kdGFgKSwgYW5kICoqU0FTKiogKGAuc2FzN2JkYXRgKS4NCg0KYGBge3J9DQoNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShoYXZlbikNCg0KIyBDU1YNCmRmX2NzdiA8LSByZWFkX2NzdigNCiAgaGVyZSgiRGF0YSIsICJkZl9TZXNzaW9uMV9lZGl0ZWQuY3N2IikNCikNCg0KIyBFeGNlbA0KZGZfZXhjZWwgPC0gcmVhZF9leGNlbCgNCiAgaGVyZSgiRGF0YSIsICJkZl9TZXNzaW9uMV9lZGl0ZWQueGxzeCIpDQopDQoNCiMgU1BTUw0KZGZfc3BzcyA8LSByZWFkX3NhdigNCiAgaGVyZSgiRGF0YSIsICJkZl9TZXNzaW9uMV9lZGl0ZWQuc2F2IikNCikNCg0KIyBTdGF0YQ0KZGZfc3RhdGEgPC0gcmVhZF9kdGEoDQogIGhlcmUoIkRhdGEiLCAiZGZfU2Vzc2lvbjFfZWRpdGVkLmR0YSIpDQopDQpgYGANCg0KIyMgKioqNC4gamFuaXRvcioqKg0KDQpVc2VkIGZvcjsNCg0KLSBDbGVhbmluZyB2YXJpYWJsZSBuYW1lcw0KDQotIFJlbW92aW5nIGR1cGxpY2F0ZSByb3dzDQoNCi0gQ3JlYXRpbmcgZnJlcXVlbmN5IHRhYmxlcw0KDQpGb3IgZXhhbXBsZSwgaW5zdGVhZCBvZiBtYW51YWxseSByZW5hbWluZyBjb2x1bW5zIGxpa2UgIndhdGVyIHNvdXJjZSIgdG8gIndhdGVyX3NvdXJjZSIsIHlvdSB1c2UgdGhlIGNvbW1hbmQgYmVsb3cgdG8gYWNoaWV2ZSB0aGUgc2FtZSB0YXNrIGZvciBhbGwgb3RoZXIgY29sdW1ucy4NCg0KYGBge3J9DQpkZiA8LSBkZiAlPiUNCiAgY2xlYW5fbmFtZXMoKQ0KDQpwcmludChuYW1lcyhkZikpDQpgYGANCg0KIyMgKioqNS4gc2tpbXIqKioNCg0KUHJvZHVjZXMgYW4gZXh0cmVtZWx5IGRldGFpbGVkIHN1bW1hcnkgY29tcHJpc2luZyBvZiB0aGUgZm9sbG93aW5nOw0KDQoqKjEuIERhdGEgRnJhbWUgT3ZlcnZpZXcgKE1ldGFkYXRhKSoqDQoNCi0gKipEaW1lbnNpb25zKio6IFRvdGFsIG51bWJlciBvZiByb3dzIGFuZCBjb2x1bW5zLg0KDQotICoqRGF0YSBUeXBlcyoqOiBBIHN0cmljdCBjb3VudCBvZiBob3cgbWFueSBjb2x1bW5zIGFyZSBudW1lcmljLCB0ZXh0LCBkYXRlcywgb3IgZmFjdG9ycy4NCg0KLSAqKkdyb3VwaW5nKio6IEluZGljYXRlcyBpZiB0aGUgZGF0YSBmcmFtZSBpcyBjdXJyZW50bHkgZ3JvdXBlZCAoZS5nLiwgdmlhIGBncm91cF9ieSgpYCkuDQoNCioqMi4gSW5mb3JtYXRpb24gZm9yIEV2ZXJ5IERhdGEgVHlwZSoqDQoNCi0gKipNaXNzaW5nbmVzcyoqOiBUaGUgZXhhY3QgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIChgbl9taXNzaW5nYCkgYW5kIHRoZSBjb21wbGV0ZSByYXRlIHBlcmNlbnRhZ2UgKGBjb21wbGV0ZV9yYXRlYCkuDQoNCi0gKipEYXRhIEZvb3RwcmludCoqOiBUb3RhbCBudW1iZXIgb2YgdW5pcXVlIHZhbHVlcyBpbnNpZGUgYSBjb2x1bW4uDQoNCioqMy4gRGVlcCBEaXZlIGZvciBUZXh0IC8gQ2hhcmFjdGVyIENvbHVtbnMqKg0KDQotICoqTGVuZ3RoIFN0YXRzKio6IFRoZSBtaW5pbXVtLCBtYXhpbXVtLCBhbmQgYXZlcmFnZSBjaGFyYWN0ZXIgbGVuZ3RoIG9mIHN0cmluZ3MgaW4gdGhhdCBjb2x1bW4uDQoNCi0gKipXaGl0ZXNwYWNlKio6IENvdW50cyBvZiBlbXB0eSBzdHJpbmdzIG9yIGhpZGRlbiBzcGFjZXMuDQoNCioqNC4gRGV0YWlsZWQgTWV0cmljcyBmb3IgTnVtZXJpYyBDb2x1bW5zKioNCg0KLSAqKkNlbnRyYWwgVGVuZGVuY3kqKjogTWVhbiAoYXZlcmFnZSkgYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4NCg0KLSAqKlBlcmNlbnRpbGVzICYgRGlzdHJpYnV0aW9uKio6IFRoZSBjb21wbGV0ZSA1LW51bWJlciBzdW1tYXJ5IChtaW5pbXVtLCAyNXRoIHBlcmNlbnRpbGUsIG1lZGlhbiwgNzV0aCBwZXJjZW50aWxlLCBhbmQgbWF4aW11bSkuDQoNCi0gKipUaGUgSW4tbGluZSBIaXN0b2dyYW0qKjogQSB0aW55LCB0ZXh0LWJhc2VkIHZpc3VhbCBzcGFya2xpbmUgZ3JhcGggc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIHNoYXBlIG9mIHlvdXIgbnVtYmVycyBkaXJlY3RseSBpbnNpZGUgeW91ciBSIGNvbnNvbGUuDQoNCiMjIyMgKioqSW5zdGVhZCBvZiBwcm9kdWNpbmcgdGhpcyBiZWxvdzsqKioNCg0KYGBge3J9DQpzdW1tYXJ5KGRmKQ0KYGBgDQoNCiMjIyMgKioqeW91IHNpbXBseSBkbyoqKg0KDQpgYGB7cn0NCnNraW0oZGYpDQpgYGANCg0KIyMgKioqNi4gbmFuaWFyKioqDQoNCkV4YW1wbGU7DQoNCiMjIyMgKipPcHRpb24gMTogbWlzc2luZ25lc3MgaGVhdG1hcCAob3IgYSBtaXNzaW5nIGRhdGEgaGVhdG1hcCkqKg0KDQpQcm9mZXNzaW9uYWwgcGFja2FnZSBmb3IgaGFuZGxpbmcgbWlzc2luZyBkYXRhLiBUaGlzIHByb2R1Y2VzIGFuIGV4Y2VsbGVudCBtaXNzaW5nIGRhdGEgdmlzdWFsaXphdGlvbi4gSXQgYWN0cyBhcyBhIHZpc3VhbCBYLXJheSBvZiB5b3VyIHNwcmVhZHNoZWV0IHRvIHNob3cgZXhhY3RseSB3aGVyZSBtaXNzaW5nIHZhbHVlcyAoYE5BYHMpIGFyZSBsb2NhdGVkLg0KDQpgYGB7cn0NCiNsaWJyYXJ5KHZpc2RhdCkNCg0KdmlzX21pc3MoZGYpDQpgYGANCg0KSGVyZSBpcyBob3cgdG8gaW50ZXJwcmV0IGl0IHN0ZXAtYnktc3RlcDoNCg0KKioxLiBUaGUgR3JpZCBTeXN0ZW0gKFJvd3MgYW5kIENvbHVtbnMpKioNCg0KLSAqKkNvbHVtbnMgKFRvcCBBeGlzKSoqOiBFYWNoIHZlcnRpY2FsIHNlY3Rpb24gcmVwcmVzZW50cyBhIGNvbHVtbiBpbiB5b3VyIGRhdGEgZnJhbWUgKGBob3VzZWhvbGRfaWRgLCBgcmVnaW9uYCwgYHdhdGVyX3NvdXJjZWAsIGBob3VzZWhvbGRfc2l6ZWAsIGBhZ2Vfb2ZfaGhfaGVhZGAsIGFuZCBgZGF0ZV9vZl9iaXJ0aGApDQoNCi0gKipSb3dzIChMZWZ0IEF4aXMpKio6IFRoZSB2ZXJ0aWNhbCBheGlzIHJlcHJlc2VudHMgeW91ciA4MCBpbmRpdmlkdWFsIHJvd3MgKG9ic2VydmF0aW9ucyksIHN0YXJ0aW5nIGF0IHJvdyAxIGF0IHRoZSB0b3AgYW5kIGdvaW5nIGRvd24gdG8gcm93IDgwIGF0IHRoZSBib3R0b20uDQoNCioqMi4gVGhlIENvbG9yIE1lYW5pbmcqKg0KDQotICoqR3JleSBibG9ja3MgKDk4LjElKSoqOiBUaGlzIHJlcHJlc2VudHMgZGF0YSB0aGF0IGlzICoqcHJlc2VudCoqIGFuZCBmaWxsZWQgaW4uIFlvdXIgZGF0YXNldCBpcyBhbG1vc3QgZW50aXJlbHkgY29tcGxldGUuDQoNCi0gKipCbGFjayBsaW5lcyAoMS45JSkqKjogVGhpcyByZXByZXNlbnRzICoqbWlzc2luZyB2YWx1ZXMgKGBOQWApKiouDQoNCioqMy4gUmVhZGluZyB0aGUgIkRhdGEgU3RvcnkiIGluIHRoaXMgR3JhcGgqKg0KDQpJbnN0ZWFkIG9mIGp1c3QgdGVsbGluZyB5b3UgKmhvdyBtYW55KiB2YWx1ZXMgYXJlIG1pc3NpbmcsIHRoaXMgZ3JhcGggdGVsbHMgeW91ICp3aGVyZSogdGhleSBhcmUgbWlzc2luZzoNCg0KLSBMb29rIGF0IHRoZSB0aW55IGJsYWNrIGhvcml6b250YWwgbGluZXMgb24gdGhlIGdyYXBoLg0KDQotIFRob3NlIGxpbmVzIHNob3cgdGhhdCBhIGNvdXBsZSBvZiBob3VzZWhvbGRzIGluIHlvdXIgZGF0YXNldCBoYXZlIG1pc3NpbmcgZGF0YSBzcGVjaWZpY2FsbHkgaW4gdGhlIGByZWdpb25gLCBgd2F0ZXJfc291cmNlYCBhbmQgYGhvdXNlaG9sZF9zaXplYCBjb2x1bW5zLg0KDQojIyMjICoqT3B0aW9uIDI6IFN3aXRjaCB0byBhIEJhciBDaGFydCAoVGhlIEJlc3QgQ2xhc3Nyb29tIEFsdGVybmF0aXZlKSoqDQoNCklmIHlvdSBhcmUganVzdCBpbnRlcmVzdGVkIHRvIHNlZSB0aGUgKip0b3RhbCBjb3VudCoqIG9mIG1pc3NpbmcgdmFsdWVzIGNsZWFybHkgd2l0aG91dCB3b3JyeWluZyBhYm91dCB0aGVpciBzcHJlYWQgaW4gcm93cywgeW91IHN3aXRjaCBmcm9tIGB2aXNfbWlzcygpYCB0byBgZ2dfbWlzc192YXIoKWAgc3RpbGwgdW5kZXIgdGhlIGBuYW5pYXJgIHBhY2thZ2UuIFRoaXMgY3JlYXRlcyBhIGNsZWFuIGJhciBjaGFydCB0aGF0IGNsZWFybHkgbGFiZWxzIGhvdyBtYW55IGBOQWBzIGFyZSBpbiBldmVyeSBzaW5nbGUgY29sdW1uDQoNCmBgYHtyfQ0KIyBDcmVhdGVzIGEgY2xlYXIgZG90L2JhciBwbG90IHNob3dpbmcgdGhlIGV4YWN0IG51bWJlciBvZiBOQXMgcGVyIGNvbHVtbg0KZ2dfbWlzc192YXIoZGYpDQpgYGANCg0KIyMgKioqNy4gbHVicmlkYXRlKioqDQoNCk1ha2VzIGRhdGVzIGVhc3kuIEluc3RlYWQgb2YgY29tcGxpY2F0ZWQgZGF0ZSBjb252ZXJzaW9ucyBsaWtlIGBhcy5EYXRlKClgOw0KDQp5b3UgY2FuIHNpbXBseSBkbyB0aGlzOw0KDQojIyMjIEZvciBgeW1kKClgDQoNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lDQogIG11dGF0ZSgNCiAgICB5bWRfZm9ybWF0ID0geW1kKGRhdGVfb2ZfYmlydGgpDQogICkNCg0KcHJpbnQoaGVhZChkZlssIGMoImRhdGVfb2ZfYmlydGgiLCAieW1kX2Zvcm1hdCIpXSwgNSkpDQpgYGANCg0KIyMjIyBGb3IgYGRteSgpYCBvciBgbWR5KClgDQoNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lDQogIG11dGF0ZSgNCiAgICBkb2JfZG15ID0gZm9ybWF0KGRhdGVfb2ZfYmlydGgsICIlZC0lbS0lWSIpLA0KICAgIGRteV9mb3JtYXQgPSBkbXkoZG9iX2RteSkNCiAgKQ0KDQpwcmludChoZWFkKGRmWywgYygiZGF0ZV9vZl9iaXJ0aCIsICJkbXlfZm9ybWF0IildLCA1KSkNCmBgYA0KDQojIyAqKio4LiBnZ3Bsb3QyKioqDQoNClRoZSAqKmBnZ3Bsb3QyYCoqIHBhY2thZ2UgaXMgdGhlIHByZW1pZXIgZGF0YSB2aXN1YWxpemF0aW9uIHRvb2xraXQgZm9yIFIsIGJ1aWx0IHRvIGNyZWF0ZSBwcm9mZXNzaW9uYWwsIHB1YmxpY2F0aW9uLXF1YWxpdHkgZ3JhcGhzLiBJdCBpcyBhdXRvbWF0aWNhbGx5IGxvYWRlZCB3aGVuZXZlciBzdHVkZW50cyBydW4gYGxpYnJhcnkodGlkeXZlcnNlKWAuIFsxXShodHRwczovL2JpbzMwNC1jbGFzcy5naXRodWIuaW8vYmlvMzA0LWJvb2svaW50cm9kdWN0aW9uLXRvLWdncGxvdDIuaHRtbCldDQoNClRoZSAiZ2ciIGluIGBnZ3Bsb3QyYCBzdGFuZHMgZm9yIHRoZSAqKkdyYW1tYXIgb2YgR3JhcGhpY3MqKi4gVGhpcyBpcyBhIHBvd2VyZnVsLCBzdHJ1Y3R1cmVkIGZyYW1ld29yayB0aGF0IGFsbG93cyB1c2VycyB0byBidWlsZCBncmFwaHMgYnkgc3RhY2tpbmcgZGlzdGluY3QsIGluZGVwZW5kZW50IGxheWVycyBvbiB0b3Agb2YgZWFjaCBvdGhlciwgcmF0aGVyIHRoYW4gdHJ5aW5nIHRvIGJ1aWxkIGEgcGxvdCBhbGwgYXQgb25jZS4gWzFdKGh0dHBzOi8vbnQyNDYuZ2l0aHViLmlvL05UUkVTLTYxMDAtZGF0YS1zY2llbmNlL2xlc3NvbjYtZ2dwbG90LXBhcnQxLmh0bWwpLCBbMl0oaHR0cHM6Ly9ycHVicy5jb20va2F6YW5qaWFuLzExNTEwMTcpLCBbM10oaHR0cHM6Ly9zdGF0c2FuZHIuY29tL2Jsb2cvZ3JhcGhpY3MtaW4tci13aXRoLWdncGxvdDIvKSwgWzRdKGh0dHBzOi8vamh1ZGF0YXNjaWVuY2Uub3JnL3RpZHl2ZXJzZWNvdXJzZS9kYXRhdml6Lmh0bWwpXQ0KDQojIyMgKipUaGUgMyBDb3JlIENvbXBvbmVudHMqKiANCg0KVG8gbWFrZSBhbnkgZ3JhcGggaW4gYGdncGxvdDJgLCBzdHVkZW50cyBvbmx5IG5lZWQgdG8gdW5kZXJzdGFuZCB0aHJlZSBmdW5kYW1lbnRhbCBjb21wb25lbnRzLiBZb3UgY2FuIHByZXNlbnQgdGhpcyBmb3JtdWxhIGluIHlvdXIgbm90ZXM6IFtbMV0oaHR0cHM6Ly9yLXN0YXRpc3RpY3MuY28vZ2dwbG90Mi1HZXR0aW5nLVN0YXJ0ZWQuaHRtbCldDQoNCi0gKioqRGF0YSArIEFlc3RoZXRpY3MgKE1hcHBpbmcpICsgR2VvbWV0cmllcyAoTGF5ZXJzKSA9IEEgR3JhcGgqKioNCg0KKioxLiBUaGUgRGF0YSoqDQoNClRoaXMgaXMgdGhlIHJhdyBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgdGhlIHZhcmlhYmxlcyB5b3Ugd2FudCB0byBwbG90IChlLmcuLCB5b3VyIGBkZmAgY29udGFpbmluZyBob3VzZWhvbGQgc2l6ZXMgYW5kIHJlZ2lvbnMpLiBbWzFdKGh0dHBzOi8vZW52aXJvbm1lbnRhbGNvbXB1dGluZy5uZXQvZ3JhcGhpY3MvZ2dwbG90L2dncGxvdC1iYXNpY3MvKV0NCg0KKioyLiBUaGUgQWVzdGhldGljcyAoYGFlc2ApKioNCg0KQWVzdGhldGljcyB0ZWxsIFIgKipob3cgdG8gY29ubmVjdCB2YXJpYWJsZXMgaW4geW91ciBkYXRhIHRvIHZpc3VhbCBwcm9wZXJ0aWVzIG9uIHRoZSBzY3JlZW4qKi4gVGhpcyBhbnN3ZXJzIHF1ZXN0aW9ucyBsaWtlOiBbWzFdKGh0dHBzOi8vcHJvZ3JhbW1pbmdoaXN0b3JpYW4ub3JnL2VuL2xlc3NvbnMvdXJiYW4tZGVtb2dyYXBoaWMtZGF0YS1yLWdncGxvdDIpXQ0KDQotIFdoYXQgdmFyaWFibGUgZ29lcyBvbiB0aGUgKipYLWF4aXMqKj8NCg0KLSBXaGF0IHZhcmlhYmxlIGdvZXMgb24gdGhlICoqWS1heGlzKio/DQoNCi0gU2hvdWxkIHRoZSBkYXRhIHBvaW50cyBiZSBjb2xvcmVkIG9yIHNpemVkIGJhc2VkIG9uIGEgc3BlY2lmaWMgY2F0ZWdvcnk/IFtbMV0oaHR0cHM6Ly9kYXRhc2NpZW5jZWJvb2suY2Evdml6Lmh0bWwpXQ0KDQoqKjMuIFRoZSBHZW9tZXRyaWVzIChgZ2VvbWApKioNCg0KR2VvbWV0cmllcyBkZWZpbmUgdGhlICoqYWN0dWFsIHNoYXBlcyoqIHRoYXQgYXBwZWFyIG9uIHRoZSBjYW52YXMgdG8gcmVwcmVzZW50IHRoZSBkYXRhLiBbWzFdKGh0dHBzOi8vd3d3LmNvZGVjYWRlbXkuY29tL2xlYXJuL2RhdGEtdmlzdWFsaXphdGlvbi1pbi1yLXNraWxsLXBhdGgvbW9kdWxlcy9nZ3Bsb3QyLWRhdGEtdmlzdWFsaXphdGlvbi13aXRoLXIvY2hlYXRzaGVldCksIFsyXShodHRwczovL2dpdGh1Yi5jb20vc3dpcmxkZXYvc3dpcmxfY291cnNlcy9ibG9iL21hc3Rlci9FeHBsb3JhdG9yeV9EYXRhX0FuYWx5c2lzL0dHUGxvdDJfUGFydDIvbGVzc29uKV0NCg0KLSBVc2UgYGdlb21fcG9pbnQoKWAgdG8gbWFrZSBhIHNjYXR0ZXIgcGxvdC4NCg0KLSBVc2UgYGdlb21fYmFyKClgIG9yIGBnZW9tX2NvbCgpYCB0byBtYWtlIGJhciBjaGFydHMuDQoNCi0gVXNlIGBnZW9tX2JveHBsb3QoKWAgdG8gbWFrZSBhIGJveC1hbmQtd2hpc2tlciBwbG90Lg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIDEuIFN0YXJ0IHdpdGggeW91ciBkYXRhIGZyYW1lDQpnZ3Bsb3QoZGF0YSA9IGRmLCBtYXBwaW5nID0gYWVzKHggPSByZWdpb24sIHkgPSBob3VzZWhvbGRfc2l6ZSwgZmlsbCA9IHJlZ2lvbikpICsgDQogIA0KICAjIDIuIERyYXcgdGhlIGJveGVzIGFuZCBtYWtlIHRoZSBsaW5lIHdpZHRocyBhIGJpdCBjbGVhbmVyDQogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuOCwgY29sb3IgPSAiYmxhY2siKSArIA0KICANCiAgIyAzLiBBcHBseSBhIHZpYnJhbnQsIHByb2Zlc3Npb25hbCBjb2xvciBwYWxldHRlIChlLmcuLCBDb2xvckJyZXdlciAiU2V0MiIpDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgDQogICMgNC4gU3RyaXAgYXdheSB0aGUgaGVhdnkgZ3JleSBiYWNrZ3JvdW5kIGZvciBhIGNsZWFuIGxvb2sNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIA0KICAjIDUuIEN1c3RvbWl6ZSB0ZXh0IGFuZCBmaXggdGhlIHRpbHRlZC9jdXQtb2ZmIGxhYmVscyBvbiB0aGUgWC1heGlzDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgICMgSGlkZXMgdGhlIHJlZHVuZGFudCBsZWdlbmQgc2luY2UgWC1heGlzIGlzIGxhYmVsZWQNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0ID0gMSwgZmFjZSA9ICJib2xkIiksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpDQogICkgKw0KICANCiAgIyA2LiBBZGQgY29tcGxldGUgbGFiZWxzDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIEhvdXNlaG9sZCBTaXplIGJ5IFJlZ2lvbiIsIA0KICAgIHggPSAiR2VvZ3JhcGhpYyBSZWdpb24iLCANCiAgICB5ID0gIk51bWJlciBvZiBQZW9wbGUgcGVyIEhvdXNlaG9sZCINCiAgKQ0KYGBgDQoNCiMjICoqOS4gU2F2aW5nIGRhdGEqKg0KDQpTYXZpbmcgdGhlIGRhdGEgb24gdG8geW91ciBsYXB0b3AgaW4gYW55IGZvcm1hdA0KDQpgYGB7cn0NCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkod3JpdGV4bCkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGhhdmVuKQ0KDQojIEV4Y2VsDQp3cml0ZV94bHN4KA0KICBkZiwNCiAgaGVyZSgiRGF0YSIsICJkZl9TZXNzaW9uMV9lZGl0ZWQueGxzeCIpDQopDQoNCiMgQ1NWDQp3cml0ZV9jc3YoDQogIGRmLA0KICBoZXJlKCJEYXRhIiwgImRmX1Nlc3Npb24xX2VkaXRlZC5jc3YiKQ0KKQ0KDQojIFNQU1MNCndyaXRlX3NhdigNCiAgZGYsDQogIGhlcmUoIkRhdGEiLCAiZGZfU2Vzc2lvbjFfZWRpdGVkLnNhdiIpDQopDQoNCiMgU3RhdGENCndyaXRlX2R0YSgNCiAgZGYsDQogIGhlcmUoIkRhdGEiLCAiZGZfU2Vzc2lvbjFfZWRpdGVkLmR0YSIpDQopDQpgYGANCg0KIyAqKlw8LS0tIEVuZCBvZiBTZXNzaW9uIOKAlFw+KioNCg==