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.
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;
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
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
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")
)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2s6IFVuZGVyc3RhbmRpbmcgWW91ciBSIExpYnJhcmllcyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgDQoNCiMjICoqQ29yZSBDb25jZXB0cyBhbmQgdGhlIExpYnJhcnkgTWV0YXBob3IqKg0KDQpUbyB1bmRlcnN0YW5kIFIsIHlvdSBtdXN0IGZpcnN0IGtub3cgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIHBhY2thZ2UgYW5kIGEgbGlicmFyeS4NCg0KQW4gKipSIHBhY2thZ2UqKiBpcyBhIHNoYXJlYWJsZSB0b29sa2l0IHRoYXQgYnVuZGxlcyB0b2dldGhlciB1c2VmdWwgZnVuY3Rpb25zLCBkYXRhLCBhbmQgdXNlciBndWlkZXMuIEFuICoqUiBsaWJyYXJ5KiogaXMgc2ltcGx5IHRoZSBmb2xkZXIgb24geW91ciBjb21wdXRlciB3aGVyZSB0aGVzZSB0b29sa2l0cyBhcmUgc3RvcmVkLiBUaGluayBvZiBhbiBSIHBhY2thZ2UgYXMgYSBzcGVjaWZpYyBib29rLCB3aGlsZSB0aGUgUiBsaWJyYXJ5IGlzIHRoZSBwaHlzaWNhbCBib29rc2hlbGYgaG9sZGluZyBhbGwgeW91ciBib29rcy4NCg0KV2hlbiB5b3Ugd29yayB3aXRoIFIsIHlvdSBzZWFyY2ggeW91ciBsaWJyYXJ5IHNoZWx2ZXMgdG8gZmluZCB0aGUgZXhhY3QgcGFja2FnZSB0b29sa2l0IHlvdSBuZWVkIGZvciB5b3VyIGRhdGEgYW5hbHlzaXMgdGFzay4NCg0KIyMgKipJbnNpZGUgYW4gUiBQYWNrYWdlKioNCg0KRXZlcnkgaW5zdGFsbGVkIHBhY2thZ2UgZm9sbG93cyBhIHN0cmljdCBmb2xkZXIgc3RydWN0dXJlIGJlaGluZCB0aGUgc2NlbmVzOw0KDQotIFRoZSBoZWFydCBvZiB0aGUgcGFja2FnZSBpcyB0aGUgYFIvYCBmb2xkZXIsIHdoaWNoIGNvbnRhaW5zIHRoZSBhY3R1YWwgY29kZSBhbmQgcHJvZ3JhbW1pbmcgZnVuY3Rpb25zLg0KDQotIFRoZSBgbWFuL2AgZm9sZGVyIGhvbGRzIHRoZSBpbnN0cnVjdGlvbiBtYW51YWxzLCB3aGljaCBwb3AgdXAgb24geW91ciBzY3JlZW4gd2hlbmV2ZXIgeW91IGFzayBSIGZvciBoZWxwIHdpdGggYSBmdW5jdGlvbi4NCg0KWW91IHdpbGwgYWxzbyBmaW5kIGEgYERFU0NSSVBUSU9OYCBmaWxlIGNvbnRhaW5pbmcgdGhlIHBhY2thZ2UgbmFtZSwgYXV0aG9yLCBhbmQgdmVyc2lvbiBkZXRhaWxzLCBhbG9uZyB3aXRoIGEgYE5BTUVTUEFDRWAgZmlsZSB0aGF0IGNvbnRyb2xzIGhvdyB0aGUgcGFja2FnZSBpbnRlcmFjdHMgd2l0aCB0aGUgcmVzdCBvZiBSLiBTb21lIHBhY2thZ2VzIGV2ZW4gaW5jbHVkZSBhIGBkYXRhL2AgZm9sZGVyIGZpbGxlZCB3aXRoIHNhbXBsZSBkYXRhc2V0cyBmb3IgcHJhY3RpY2UuDQoNCiMjICoqV2hlcmUgUGFja2FnZXMgTGl2ZSoqDQoNCkJlZm9yZSB5b3UgY2FuIHVzZSBhIHBhY2thZ2UsIHlvdSBtdXN0IGRvd25sb2FkIGl0IGZyb20gYW4gb25saW5lIHN0b3JhZ2UgcmVwb3NpdG9yeS4gVGhlIG1vc3QgaW1wb3J0YW50IG9uZSBpcyAqKkNSQU4qKiwgdGhlIENvbXByZWhlbnNpdmUgUiBBcmNoaXZlIE5ldHdvcmsuDQoNCkNSQU4gaXMgdGhlIG9mZmljaWFsIG1hcmtldHBsYWNlIGZvciBSIHBhY2thZ2VzLCBob3N0aW5nIHRlbnMgb2YgdGhvdXNhbmRzIG9mIG9wdGlvbnMgdGhhdCBtdXN0IHBhc3Mgc3RyaWN0IHF1YWxpdHkgY2hlY2tzLiBGb3Igc3BlY2lhbGl6ZWQgZmllbGRzIGxpa2UgYmlvbG9neSBhbmQgZ2VuZXRpY3MsIHNjaWVudGlzdHMgdXNlIGEgZGVkaWNhdGVkIHJlcG9zaXRvcnkgY2FsbGVkICoqQmlvY29uZHVjdG9yKiouDQoNCkZpbmFsbHksIGRldmVsb3BlcnMgb2Z0ZW4gc2hhcmUgZWFybHksIGV4cGVyaW1lbnRhbCB2ZXJzaW9ucyBvZiB0aGVpciB0b29sa2l0cyBvbiBvcGVuIHBsYXRmb3JtcyBsaWtlICoqR2l0SHViKiogYmVmb3JlIG9mZmljaWFsIHJlbGVhc2UuDQoNCiMjICoqTWFuYWdpbmcgWW91ciBUb29sa2l0cyoqDQoNCllvdSBpbnRlcmFjdCB3aXRoIHlvdXIgUiBsaWJyYXJ5IHVzaW5nIGEgZmV3IGJhc2ljIGNvbW1hbmRzOw0KDQotIFRvIGRvd25sb2FkIGEgbmV3IHRvb2xraXQgZnJvbSBDUkFOLCB5b3UgdXNlIGBpbnN0YWxsLnBhY2thZ2VzKClgLiBPbmNlIGl0IGlzIG9uIHlvdXIgY29tcHV0ZXIsIHlvdSBtdXN0IHJ1biBgbGlicmFyeSgpYCBhdCB0aGUgc3RhcnQgb2YgeW91ciBzY3JpcHQgdG8gb3BlbiB0aGF0IHRvb2xraXQgYW5kIG1ha2UgaXRzIHRvb2xzIGFjdGl2ZSBmb3IgeW91ciBjdXJyZW50IHNlc3Npb24uDQoNCklmIHlvdSB3YW50IHRvIHNlZSBleGFjdGx5IHdoZXJlIHlvdXIgbGlicmFyeSBmb2xkZXIgaXMgaGlkZGVuIG9uIHlvdXIgY29tcHV0ZXIsIHR5cGUgYC5saWJQYXRocygpYC4gWW91IGNhbiBhbHNvIHJ1biBgaW5zdGFsbGVkLnBhY2thZ2VzKClgIHRvIHNlZSBhIGNvbXBsZXRlIGNoZWNrbGlzdCBvZiBldmVyeXRoaW5nIHlvdSBoYXZlIGluc3RhbGxlZCwgb3IgYHVwZGF0ZS5wYWNrYWdlcygpYCB0byBlbnN1cmUgYWxsIHlvdXIgdG9vbHMgYXJlIHJ1bm5pbmcgb24gdGhlIGxhdGVzdCB2ZXJzaW9ucy4NCg0KIyMgKipJbnN0YWxsaW5nICYgTG9hZGluZyBMaWJyYXJpZXMqKg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgSW5zdGFsbCBwYWNrYWdlcyAoUnVuIG9ubHkgb25jZSkNCg0KIyBpbnN0YWxsLnBhY2thZ2VzKGMoDQojICAgInRpZHl2ZXJzZSIsDQojICAgInJlYWR4bCIsDQojICAgImphbml0b3IiLA0KIyAgICJza2ltciIsDQojICAgIm5hbmlhciIsDQojICAgImx1YnJpZGF0ZSIsDQojICAgInN0cmluZ3IiLA0KIyAgICJoZXJlIiwNCiMgICAid3JpdGV4bCIsDQojICAgInBzeWNoIg0KIyApKQ0KDQpgYGANCg0KTm93IGxvYWQgdGhlbS4NCg0KYGBge3IsIGVjaG89RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShza2ltcikNCmxpYnJhcnkobmFuaWFyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KHdyaXRleGwpDQpsaWJyYXJ5KHBzeWNoKQ0KDQpgYGANCg0KIyMgKipEZWZpbmluZyBQYWNrYWdlcyoqDQoNCiMjICoqKjEuIHRpZHl2ZXJzZSoqKiAqKkNvbnRhaW5zKioNCg0KVGhlIHRpZHl2ZXJzZSBwcm92aWRlcyBhbG1vc3QgZXZlcnl0aGluZyBuZWVkZWQgZm9yIG1vZGVybiBkYXRhIGNsZWFuaW5nIGFuZCBtYW5pcHVsYXRpb24uIEl0IGNvbnRhaW5zOw0KDQotIGRwbHlyLCB0aWR5ciwgZ2dwbG90MiwgcmVhZHIsIHRpYmJsZSwgcHVycnIsIGZvcmNhdHMsIHN0cmluZ3INCg0KVGhpcyBpcyB0aGUgYmFja2JvbmUgb2YgbW9kZXJuIFIuDQoNCiMjICoqKjIuIGhlcmUqKioNCg0KT25lIG9mIHRoZSBtb3N0IGltcG9ydGFudCBwYWNrYWdlcy4gSXQgbWFrZXMgeW91ciBwcm9qZWN0IHJlcHJvZHVjaWJsZSBvbiBhbnkgY29tcHV0ZXIuDQoNCldoZW4gc2hhcmluZyBSIHNjcmlwdHMsIHBlb3BsZSB1c3VhbGx5IGhhcmRjb2RlIHRoZSBmaWxlX3BhdGhzIHRvIGZpbGVzIG9uIHRoZWlyIHNwZWNpZmljIGNvbXB1dGVyIG9yIHVzZSBgc2V0d2QoIkM6L015Rm9sZGVyIilgLiBXaGVuIGFub3RoZXIgcGVyc29uL3N0dWRlbnQgb3IgYSB0ZWFjaGVyIHRyaWVzIHRvIHJ1biB0aGF0IGNvZGUgb24gYSBkaWZmZXJlbnQgY29tcHV0ZXIsIHRoZSBzY3JpcHQgYnJlYWtzIGluc3RhbnRseSBiZWNhdXNlIHRoYXQgZXhhY3QgZmlsZSBwYXRoIGRvZXMgbm90IGV4aXN0Lg0KDQojIyMgKipUaGUgT2xkLCBCcm9rZW4gV2F5IChBdm9pZCBUaGlzKSoqDQoNCklmIHlvdSBtb3ZlIHRoaXMgZmlsZSB0byBhbm90aGVyIGNvbXB1dGVyLCBpdCBjcmFzaGVzIGltbWVkaWF0ZWx5DQoNCmBgYHtyfQ0KIyBUaGlzIHdpbGwgYnJlYWsgb24geW91ciBzdHVkZW50cycgb3IgZ3JhZGluZyBjb21wdXRlcnMNCg0Kc2V0d2QoIkM6L1VzZXJzL0VybmVzdC9TZXNzaW9uMS9SX0d1aWRlIikNCg0KZGYgPC0gcmVhZF94bHN4KCJEYXRhL0RhdGExX1Nlc3Npb24xLnhsc3giKQ0KYGBgDQoNCiMjIyAqKlRoZSBtb2Rlcm4gd2F5KioNCg0KVGhpcyBjb2RlIHdvcmtzIHBlcmZlY3RseSBvbiBNYWMsIFdpbmRvd3MsIExpbnV4LCBjbG91ZCBzZXJ2ZXJzLCBhbmQgYW55IHN0dWRlbnQncyBtYWNoaW5lLiBUaGUgMXN0IGVudHJ5ICJEYXRhIiAtIGlzIHRoZSBmb2xkZXIgd2hlcmUgdGhlIGRhdGEgaXMgc3RvcmVkLCBhbmQgdGhlIDJuZCBlbnRyeSAiRGF0YTFfU2Vzc2lvbjEueGxzeCIgLSBpcyB0aGUgZXhhY3QgZGF0YSB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4NCg0KYGBge3J9DQoNCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkocmVhZHIpDQoNCiMgVGhpcyBhdXRvbWF0aWNhbGx5IGZpbmRzIHRoZSBwcm9qZWN0IHJvb3QgYW5kIGJ1aWxkcyB0aGUgY29ycmVjdCBwYXRoDQoNCmRmIDwtIHJlYWRfeGxzeChoZXJlKCJEYXRhIiwgIkRhdGExX1Nlc3Npb24xLnhsc3giKSwgc2hlZXQgPSAiU2hlZXQyIikNCg0KcHJpbnQoZGltKGRmKSkNCnByaW50KG5hbWVzKGRmKSkNCmBgYA0KDQoqKmBoZXJlYCoqOiBEb2VzIG5vdCBpbXBvcnQgZGF0YSBpdHNlbGYsIGJ1dCBzYWZlbHkgY2FsY3VsYXRlcyB0aGUgZXhhY3QgKipmb2xkZXIgcGF0aCoqIHRvIHlvdXIgZmlsZXMgc28gdGhlIG90aGVyIHBhY2thZ2VzIGNhbiBvcGVuIHRoZW0gd2l0aG91dCBicmVha2luZy4NCg0KIyMgKioqMy4gcmVhZHhsLCBoYXZlbioqKg0KDQpUbyBicmluZyBleHRlcm5hbCBmaWxlcyBpbnRvIFIsIHdlIHJlbHkgb24gYSBzcGVjaWFsaXplZCB0ZWFtIG9mIHBhY2thZ2VzLiBUb2dldGhlciwgdGhleSBhbGxvdyB1cyB0byBpbXBvcnQgYWxtb3N0IGFueSBjb21tb24gZGF0YSBmb3JtYXQgeW91IHdpbGwgZW5jb3VudGVyOg0KDQotICoqYHJlYWRyYCoqICh2aWEgKipgdGlkeXZlcnNlYCoqKTogQnVpbHQgZm9yIHJlYWRpbmcgZmxhdCB0ZXh0IGZpbGVzIGxpa2UgKipDU1YqKiAoY29tbWEtc2VwYXJhdGVkKSwgVFNWICh0YWItc2VwYXJhdGVkKSwgYW5kIHBsYWluIHRleHQgbG9ncy4NCg0KLSAqKmByZWFkeGxgKio6IFNwZWNpYWxpemVkIGZvciBleHRyYWN0aW5nIHRhYnVsYXIgZGF0YSBkaXJlY3RseSBvdXQgb2YgTWljcm9zb2Z0ICoqRXhjZWwqKiBzcHJlYWRzaGVldHMgKGAueGxzYCBhbmQgYC54bHN4YCkuDQoNCi0gKipgaGF2ZW5gKio6IEJ1aWx0IHRvIHJlYWQgYW5kIHdyaXRlIGRhdGEgZmlsZXMgZ2VuZXJhdGVkIGJ5IG90aGVyIG1ham9yIHN0YXRpc3RpY2FsIHNvZnR3YXJlLCBzcGVjaWZpY2FsbHkgKipTUFNTKiogKGAuc2F2YCksICoqU3RhdGEqKiAoYC5kdGFgKSwgYW5kICoqU0FTKiogKGAuc2FzN2JkYXRgKS4NCg0KYGBge3J9DQoNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShoYXZlbikNCg0KIyBDU1YNCmRmX2NzdiA8LSByZWFkX2NzdigNCiAgaGVyZSgiRGF0YSIsICJkZl9TZXNzaW9uMV9lZGl0ZWQuY3N2IikNCikNCg0KIyBFeGNlbA0KZGZfZXhjZWwgPC0gcmVhZF9leGNlbCgNCiAgaGVyZSgiRGF0YSIsICJkZl9TZXNzaW9uMV9lZGl0ZWQueGxzeCIpDQopDQoNCiMgU1BTUw0KZGZfc3BzcyA8LSByZWFkX3NhdigNCiAgaGVyZSgiRGF0YSIsICJkZl9TZXNzaW9uMV9lZGl0ZWQuc2F2IikNCikNCg0KIyBTdGF0YQ0KZGZfc3RhdGEgPC0gcmVhZF9kdGEoDQogIGhlcmUoIkRhdGEiLCAiZGZfU2Vzc2lvbjFfZWRpdGVkLmR0YSIpDQopDQpgYGANCg0KIyMgKioqNC4gamFuaXRvcioqKg0KDQpVc2VkIGZvcjsNCg0KLSBDbGVhbmluZyB2YXJpYWJsZSBuYW1lcw0KDQotIFJlbW92aW5nIGR1cGxpY2F0ZSByb3dzDQoNCi0gQ3JlYXRpbmcgZnJlcXVlbmN5IHRhYmxlcw0KDQpGb3IgZXhhbXBsZSwgaW5zdGVhZCBvZiBtYW51YWxseSByZW5hbWluZyBjb2x1bW5zIGxpa2UgIndhdGVyIHNvdXJjZSIgdG8gIndhdGVyX3NvdXJjZSIsIHlvdSB1c2UgdGhlIGNvbW1hbmQgYmVsb3cgdG8gYWNoaWV2ZSB0aGUgc2FtZSB0YXNrIGZvciBhbGwgb3RoZXIgY29sdW1ucy4NCg0KYGBge3J9DQpkZiA8LSBkZiAlPiUNCiAgY2xlYW5fbmFtZXMoKQ0KDQpwcmludChuYW1lcyhkZikpDQpgYGANCg0KIyMgKioqNS4gc2tpbXIqKioNCg0KUHJvZHVjZXMgYW4gZXh0cmVtZWx5IGRldGFpbGVkIHN1bW1hcnkgY29tcHJpc2luZyBvZiB0aGUgZm9sbG93aW5nOw0KDQoqKjEuIERhdGEgRnJhbWUgT3ZlcnZpZXcgKE1ldGFkYXRhKSoqDQoNCi0gKipEaW1lbnNpb25zKio6IFRvdGFsIG51bWJlciBvZiByb3dzIGFuZCBjb2x1bW5zLg0KDQotICoqRGF0YSBUeXBlcyoqOiBBIHN0cmljdCBjb3VudCBvZiBob3cgbWFueSBjb2x1bW5zIGFyZSBudW1lcmljLCB0ZXh0LCBkYXRlcywgb3IgZmFjdG9ycy4NCg0KLSAqKkdyb3VwaW5nKio6IEluZGljYXRlcyBpZiB0aGUgZGF0YSBmcmFtZSBpcyBjdXJyZW50bHkgZ3JvdXBlZCAoZS5nLiwgdmlhIGBncm91cF9ieSgpYCkuDQoNCioqMi4gSW5mb3JtYXRpb24gZm9yIEV2ZXJ5IERhdGEgVHlwZSoqDQoNCi0gKipNaXNzaW5nbmVzcyoqOiBUaGUgZXhhY3QgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIChgbl9taXNzaW5nYCkgYW5kIHRoZSBjb21wbGV0ZSByYXRlIHBlcmNlbnRhZ2UgKGBjb21wbGV0ZV9yYXRlYCkuDQoNCi0gKipEYXRhIEZvb3RwcmludCoqOiBUb3RhbCBudW1iZXIgb2YgdW5pcXVlIHZhbHVlcyBpbnNpZGUgYSBjb2x1bW4uDQoNCioqMy4gRGVlcCBEaXZlIGZvciBUZXh0IC8gQ2hhcmFjdGVyIENvbHVtbnMqKg0KDQotICoqTGVuZ3RoIFN0YXRzKio6IFRoZSBtaW5pbXVtLCBtYXhpbXVtLCBhbmQgYXZlcmFnZSBjaGFyYWN0ZXIgbGVuZ3RoIG9mIHN0cmluZ3MgaW4gdGhhdCBjb2x1bW4uDQoNCi0gKipXaGl0ZXNwYWNlKio6IENvdW50cyBvZiBlbXB0eSBzdHJpbmdzIG9yIGhpZGRlbiBzcGFjZXMuDQoNCioqNC4gRGV0YWlsZWQgTWV0cmljcyBmb3IgTnVtZXJpYyBDb2x1bW5zKioNCg0KLSAqKkNlbnRyYWwgVGVuZGVuY3kqKjogTWVhbiAoYXZlcmFnZSkgYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4NCg0KLSAqKlBlcmNlbnRpbGVzICYgRGlzdHJpYnV0aW9uKio6IFRoZSBjb21wbGV0ZSA1LW51bWJlciBzdW1tYXJ5IChtaW5pbXVtLCAyNXRoIHBlcmNlbnRpbGUsIG1lZGlhbiwgNzV0aCBwZXJjZW50aWxlLCBhbmQgbWF4aW11bSkuDQoNCi0gKipUaGUgSW4tbGluZSBIaXN0b2dyYW0qKjogQSB0aW55LCB0ZXh0LWJhc2VkIHZpc3VhbCBzcGFya2xpbmUgZ3JhcGggc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIHNoYXBlIG9mIHlvdXIgbnVtYmVycyBkaXJlY3RseSBpbnNpZGUgeW91ciBSIGNvbnNvbGUuDQoNCiMjIyMgKioqSW5zdGVhZCBvZiBwcm9kdWNpbmcgdGhpcyBiZWxvdzsqKioNCg0KYGBge3J9DQpzdW1tYXJ5KGRmKQ0KYGBgDQoNCiMjIyMgKioqeW91IHNpbXBseSBkbyoqKg0KDQpgYGB7cn0NCnNraW0oZGYpDQpgYGANCg0KIyMgKioqNi4gbmFuaWFyKioqDQoNCkV4YW1wbGU7DQoNCiMjIyMgKipPcHRpb24gMTogbWlzc2luZ25lc3MgaGVhdG1hcCAob3IgYSBtaXNzaW5nIGRhdGEgaGVhdG1hcCkqKg0KDQpQcm9mZXNzaW9uYWwgcGFja2FnZSBmb3IgaGFuZGxpbmcgbWlzc2luZyBkYXRhLiBUaGlzIHByb2R1Y2VzIGFuIGV4Y2VsbGVudCBtaXNzaW5nIGRhdGEgdmlzdWFsaXphdGlvbi4gSXQgYWN0cyBhcyBhIHZpc3VhbCBYLXJheSBvZiB5b3VyIHNwcmVhZHNoZWV0IHRvIHNob3cgZXhhY3RseSB3aGVyZSBtaXNzaW5nIHZhbHVlcyAoYE5BYHMpIGFyZSBsb2NhdGVkLg0KDQpgYGB7cn0NCiNsaWJyYXJ5KHZpc2RhdCkNCg0KdmlzX21pc3MoZGYpDQpgYGANCg0KSGVyZSBpcyBob3cgdG8gaW50ZXJwcmV0IGl0IHN0ZXAtYnktc3RlcDoNCg0KKioxLiBUaGUgR3JpZCBTeXN0ZW0gKFJvd3MgYW5kIENvbHVtbnMpKioNCg0KLSAqKkNvbHVtbnMgKFRvcCBBeGlzKSoqOiBFYWNoIHZlcnRpY2FsIHNlY3Rpb24gcmVwcmVzZW50cyBhIGNvbHVtbiBpbiB5b3VyIGRhdGEgZnJhbWUgKGBob3VzZWhvbGRfaWRgLCBgcmVnaW9uYCwgYHdhdGVyX3NvdXJjZWAsIGBob3VzZWhvbGRfc2l6ZWAsIGBhZ2Vfb2ZfaGhfaGVhZGAsIGFuZCBgZGF0ZV9vZl9iaXJ0aGApDQoNCi0gKipSb3dzIChMZWZ0IEF4aXMpKio6IFRoZSB2ZXJ0aWNhbCBheGlzIHJlcHJlc2VudHMgeW91ciA4MCBpbmRpdmlkdWFsIHJvd3MgKG9ic2VydmF0aW9ucyksIHN0YXJ0aW5nIGF0IHJvdyAxIGF0IHRoZSB0b3AgYW5kIGdvaW5nIGRvd24gdG8gcm93IDgwIGF0IHRoZSBib3R0b20uDQoNCioqMi4gVGhlIENvbG9yIE1lYW5pbmcqKg0KDQotICoqR3JleSBibG9ja3MgKDk4LjElKSoqOiBUaGlzIHJlcHJlc2VudHMgZGF0YSB0aGF0IGlzICoqcHJlc2VudCoqIGFuZCBmaWxsZWQgaW4uIFlvdXIgZGF0YXNldCBpcyBhbG1vc3QgZW50aXJlbHkgY29tcGxldGUuDQoNCi0gKipCbGFjayBsaW5lcyAoMS45JSkqKjogVGhpcyByZXByZXNlbnRzICoqbWlzc2luZyB2YWx1ZXMgKGBOQWApKiouDQoNCioqMy4gUmVhZGluZyB0aGUgIkRhdGEgU3RvcnkiIGluIHRoaXMgR3JhcGgqKg0KDQpJbnN0ZWFkIG9mIGp1c3QgdGVsbGluZyB5b3UgKmhvdyBtYW55KiB2YWx1ZXMgYXJlIG1pc3NpbmcsIHRoaXMgZ3JhcGggdGVsbHMgeW91ICp3aGVyZSogdGhleSBhcmUgbWlzc2luZzoNCg0KLSBMb29rIGF0IHRoZSB0aW55IGJsYWNrIGhvcml6b250YWwgbGluZXMgb24gdGhlIGdyYXBoLg0KDQotIFRob3NlIGxpbmVzIHNob3cgdGhhdCBhIGNvdXBsZSBvZiBob3VzZWhvbGRzIGluIHlvdXIgZGF0YXNldCBoYXZlIG1pc3NpbmcgZGF0YSBzcGVjaWZpY2FsbHkgaW4gdGhlIGByZWdpb25gLCBgd2F0ZXJfc291cmNlYCBhbmQgYGhvdXNlaG9sZF9zaXplYCBjb2x1bW5zLg0KDQojIyMjICoqT3B0aW9uIDI6IFN3aXRjaCB0byBhIEJhciBDaGFydCAoVGhlIEJlc3QgQ2xhc3Nyb29tIEFsdGVybmF0aXZlKSoqDQoNCklmIHlvdSBhcmUganVzdCBpbnRlcmVzdGVkIHRvIHNlZSB0aGUgKip0b3RhbCBjb3VudCoqIG9mIG1pc3NpbmcgdmFsdWVzIGNsZWFybHkgd2l0aG91dCB3b3JyeWluZyBhYm91dCB0aGVpciBzcHJlYWQgaW4gcm93cywgeW91IHN3aXRjaCBmcm9tIGB2aXNfbWlzcygpYCB0byBgZ2dfbWlzc192YXIoKWAgc3RpbGwgdW5kZXIgdGhlIGBuYW5pYXJgIHBhY2thZ2UuIFRoaXMgY3JlYXRlcyBhIGNsZWFuIGJhciBjaGFydCB0aGF0IGNsZWFybHkgbGFiZWxzIGhvdyBtYW55IGBOQWBzIGFyZSBpbiBldmVyeSBzaW5nbGUgY29sdW1uDQoNCmBgYHtyfQ0KIyBDcmVhdGVzIGEgY2xlYXIgZG90L2JhciBwbG90IHNob3dpbmcgdGhlIGV4YWN0IG51bWJlciBvZiBOQXMgcGVyIGNvbHVtbg0KZ2dfbWlzc192YXIoZGYpDQpgYGANCg0KIyMgKioqNy4gbHVicmlkYXRlKioqDQoNCk1ha2VzIGRhdGVzIGVhc3kuIEluc3RlYWQgb2YgY29tcGxpY2F0ZWQgZGF0ZSBjb252ZXJzaW9ucyBsaWtlIGBhcy5EYXRlKClgOw0KDQp5b3UgY2FuIHNpbXBseSBkbyB0aGlzOw0KDQojIyMjIEZvciBgeW1kKClgDQoNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lDQogIG11dGF0ZSgNCiAgICB5bWRfZm9ybWF0ID0geW1kKGRhdGVfb2ZfYmlydGgpDQogICkNCg0KcHJpbnQoaGVhZChkZlssIGMoImRhdGVfb2ZfYmlydGgiLCAieW1kX2Zvcm1hdCIpXSwgNSkpDQpgYGANCg0KIyMjIyBGb3IgYGRteSgpYCBvciBgbWR5KClgDQoNCmBgYHtyfQ0KZGYgPC0gZGYgJT4lDQogIG11dGF0ZSgNCiAgICBkb2JfZG15ID0gZm9ybWF0KGRhdGVfb2ZfYmlydGgsICIlZC0lbS0lWSIpLA0KICAgIGRteV9mb3JtYXQgPSBkbXkoZG9iX2RteSkNCiAgKQ0KDQpwcmludChoZWFkKGRmWywgYygiZGF0ZV9vZl9iaXJ0aCIsICJkbXlfZm9ybWF0IildLCA1KSkNCmBgYA0KDQojIyAqKio4LiBnZ3Bsb3QyKioqDQoNClRoZSAqKmBnZ3Bsb3QyYCoqIHBhY2thZ2UgaXMgdGhlIHByZW1pZXIgZGF0YSB2aXN1YWxpemF0aW9uIHRvb2xraXQgZm9yIFIsIGJ1aWx0IHRvIGNyZWF0ZSBwcm9mZXNzaW9uYWwsIHB1YmxpY2F0aW9uLXF1YWxpdHkgZ3JhcGhzLiBJdCBpcyBhdXRvbWF0aWNhbGx5IGxvYWRlZCB3aGVuZXZlciBzdHVkZW50cyBydW4gYGxpYnJhcnkodGlkeXZlcnNlKWAuIFsxXShodHRwczovL2JpbzMwNC1jbGFzcy5naXRodWIuaW8vYmlvMzA0LWJvb2svaW50cm9kdWN0aW9uLXRvLWdncGxvdDIuaHRtbCldDQoNClRoZSAiZ2ciIGluIGBnZ3Bsb3QyYCBzdGFuZHMgZm9yIHRoZSAqKkdyYW1tYXIgb2YgR3JhcGhpY3MqKi4gVGhpcyBpcyBhIHBvd2VyZnVsLCBzdHJ1Y3R1cmVkIGZyYW1ld29yayB0aGF0IGFsbG93cyB1c2VycyB0byBidWlsZCBncmFwaHMgYnkgc3RhY2tpbmcgZGlzdGluY3QsIGluZGVwZW5kZW50IGxheWVycyBvbiB0b3Agb2YgZWFjaCBvdGhlciwgcmF0aGVyIHRoYW4gdHJ5aW5nIHRvIGJ1aWxkIGEgcGxvdCBhbGwgYXQgb25jZS4gWzFdKGh0dHBzOi8vbnQyNDYuZ2l0aHViLmlvL05UUkVTLTYxMDAtZGF0YS1zY2llbmNlL2xlc3NvbjYtZ2dwbG90LXBhcnQxLmh0bWwpLCBbMl0oaHR0cHM6Ly9ycHVicy5jb20va2F6YW5qaWFuLzExNTEwMTcpLCBbM10oaHR0cHM6Ly9zdGF0c2FuZHIuY29tL2Jsb2cvZ3JhcGhpY3MtaW4tci13aXRoLWdncGxvdDIvKSwgWzRdKGh0dHBzOi8vamh1ZGF0YXNjaWVuY2Uub3JnL3RpZHl2ZXJzZWNvdXJzZS9kYXRhdml6Lmh0bWwpXQ0KDQojIyMgKipUaGUgMyBDb3JlIENvbXBvbmVudHMqKiANCg0KVG8gbWFrZSBhbnkgZ3JhcGggaW4gYGdncGxvdDJgLCBzdHVkZW50cyBvbmx5IG5lZWQgdG8gdW5kZXJzdGFuZCB0aHJlZSBmdW5kYW1lbnRhbCBjb21wb25lbnRzLiBZb3UgY2FuIHByZXNlbnQgdGhpcyBmb3JtdWxhIGluIHlvdXIgbm90ZXM6IFtbMV0oaHR0cHM6Ly9yLXN0YXRpc3RpY3MuY28vZ2dwbG90Mi1HZXR0aW5nLVN0YXJ0ZWQuaHRtbCldDQoNCi0gKioqRGF0YSArIEFlc3RoZXRpY3MgKE1hcHBpbmcpICsgR2VvbWV0cmllcyAoTGF5ZXJzKSA9IEEgR3JhcGgqKioNCg0KKioxLiBUaGUgRGF0YSoqDQoNClRoaXMgaXMgdGhlIHJhdyBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgdGhlIHZhcmlhYmxlcyB5b3Ugd2FudCB0byBwbG90IChlLmcuLCB5b3VyIGBkZmAgY29udGFpbmluZyBob3VzZWhvbGQgc2l6ZXMgYW5kIHJlZ2lvbnMpLiBbWzFdKGh0dHBzOi8vZW52aXJvbm1lbnRhbGNvbXB1dGluZy5uZXQvZ3JhcGhpY3MvZ2dwbG90L2dncGxvdC1iYXNpY3MvKV0NCg0KKioyLiBUaGUgQWVzdGhldGljcyAoYGFlc2ApKioNCg0KQWVzdGhldGljcyB0ZWxsIFIgKipob3cgdG8gY29ubmVjdCB2YXJpYWJsZXMgaW4geW91ciBkYXRhIHRvIHZpc3VhbCBwcm9wZXJ0aWVzIG9uIHRoZSBzY3JlZW4qKi4gVGhpcyBhbnN3ZXJzIHF1ZXN0aW9ucyBsaWtlOiBbWzFdKGh0dHBzOi8vcHJvZ3JhbW1pbmdoaXN0b3JpYW4ub3JnL2VuL2xlc3NvbnMvdXJiYW4tZGVtb2dyYXBoaWMtZGF0YS1yLWdncGxvdDIpXQ0KDQotIFdoYXQgdmFyaWFibGUgZ29lcyBvbiB0aGUgKipYLWF4aXMqKj8NCg0KLSBXaGF0IHZhcmlhYmxlIGdvZXMgb24gdGhlICoqWS1heGlzKio/DQoNCi0gU2hvdWxkIHRoZSBkYXRhIHBvaW50cyBiZSBjb2xvcmVkIG9yIHNpemVkIGJhc2VkIG9uIGEgc3BlY2lmaWMgY2F0ZWdvcnk/IFtbMV0oaHR0cHM6Ly9kYXRhc2NpZW5jZWJvb2suY2Evdml6Lmh0bWwpXQ0KDQoqKjMuIFRoZSBHZW9tZXRyaWVzIChgZ2VvbWApKioNCg0KR2VvbWV0cmllcyBkZWZpbmUgdGhlICoqYWN0dWFsIHNoYXBlcyoqIHRoYXQgYXBwZWFyIG9uIHRoZSBjYW52YXMgdG8gcmVwcmVzZW50IHRoZSBkYXRhLiBbWzFdKGh0dHBzOi8vd3d3LmNvZGVjYWRlbXkuY29tL2xlYXJuL2RhdGEtdmlzdWFsaXphdGlvbi1pbi1yLXNraWxsLXBhdGgvbW9kdWxlcy9nZ3Bsb3QyLWRhdGEtdmlzdWFsaXphdGlvbi13aXRoLXIvY2hlYXRzaGVldCksIFsyXShodHRwczovL2dpdGh1Yi5jb20vc3dpcmxkZXYvc3dpcmxfY291cnNlcy9ibG9iL21hc3Rlci9FeHBsb3JhdG9yeV9EYXRhX0FuYWx5c2lzL0dHUGxvdDJfUGFydDIvbGVzc29uKV0NCg0KLSBVc2UgYGdlb21fcG9pbnQoKWAgdG8gbWFrZSBhIHNjYXR0ZXIgcGxvdC4NCg0KLSBVc2UgYGdlb21fYmFyKClgIG9yIGBnZW9tX2NvbCgpYCB0byBtYWtlIGJhciBjaGFydHMuDQoNCi0gVXNlIGBnZW9tX2JveHBsb3QoKWAgdG8gbWFrZSBhIGJveC1hbmQtd2hpc2tlciBwbG90Lg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIDEuIFN0YXJ0IHdpdGggeW91ciBkYXRhIGZyYW1lDQpnZ3Bsb3QoZGF0YSA9IGRmLCBtYXBwaW5nID0gYWVzKHggPSByZWdpb24sIHkgPSBob3VzZWhvbGRfc2l6ZSwgZmlsbCA9IHJlZ2lvbikpICsgDQogIA0KICAjIDIuIERyYXcgdGhlIGJveGVzIGFuZCBtYWtlIHRoZSBsaW5lIHdpZHRocyBhIGJpdCBjbGVhbmVyDQogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuOCwgY29sb3IgPSAiYmxhY2siKSArIA0KICANCiAgIyAzLiBBcHBseSBhIHZpYnJhbnQsIHByb2Zlc3Npb25hbCBjb2xvciBwYWxldHRlIChlLmcuLCBDb2xvckJyZXdlciAiU2V0MiIpDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgDQogICMgNC4gU3RyaXAgYXdheSB0aGUgaGVhdnkgZ3JleSBiYWNrZ3JvdW5kIGZvciBhIGNsZWFuIGxvb2sNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIA0KICAjIDUuIEN1c3RvbWl6ZSB0ZXh0IGFuZCBmaXggdGhlIHRpbHRlZC9jdXQtb2ZmIGxhYmVscyBvbiB0aGUgWC1heGlzDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgICMgSGlkZXMgdGhlIHJlZHVuZGFudCBsZWdlbmQgc2luY2UgWC1heGlzIGlzIGxhYmVsZWQNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0ID0gMSwgZmFjZSA9ICJib2xkIiksDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpDQogICkgKw0KICANCiAgIyA2LiBBZGQgY29tcGxldGUgbGFiZWxzDQogIGxhYnMoDQogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIEhvdXNlaG9sZCBTaXplIGJ5IFJlZ2lvbiIsIA0KICAgIHggPSAiR2VvZ3JhcGhpYyBSZWdpb24iLCANCiAgICB5ID0gIk51bWJlciBvZiBQZW9wbGUgcGVyIEhvdXNlaG9sZCINCiAgKQ0KYGBgDQoNCiMjICoqOS4gU2F2aW5nIGRhdGEqKg0KDQpTYXZpbmcgdGhlIGRhdGEgb24gdG8geW91ciBsYXB0b3AgaW4gYW55IGZvcm1hdA0KDQpgYGB7cn0NCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkod3JpdGV4bCkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGhhdmVuKQ0KDQojIEV4Y2VsDQp3cml0ZV94bHN4KA0KICBkZiwNCiAgaGVyZSgiRGF0YSIsICJkZl9TZXNzaW9uMV9lZGl0ZWQueGxzeCIpDQopDQoNCiMgQ1NWDQp3cml0ZV9jc3YoDQogIGRmLA0KICBoZXJlKCJEYXRhIiwgImRmX1Nlc3Npb24xX2VkaXRlZC5jc3YiKQ0KKQ0KDQojIFNQU1MNCndyaXRlX3NhdigNCiAgZGYsDQogIGhlcmUoIkRhdGEiLCAiZGZfU2Vzc2lvbjFfZWRpdGVkLnNhdiIpDQopDQoNCiMgU3RhdGENCndyaXRlX2R0YSgNCiAgZGYsDQogIGhlcmUoIkRhdGEiLCAiZGZfU2Vzc2lvbjFfZWRpdGVkLmR0YSIpDQopDQpgYGANCg0KIyAqKlw8LS0tIEVuZCBvZiBTZXNzaW9uIOKAlFw+KioNCg==