Overview

This tutorial will show you you how to map the latest tree-status in subquadrats with map_tag().

Motivation

Imagine you need some maps to work in the field at the site Yosemite. All you need is a ViewFullTable and the function map_tag(). Here is a glimpse of your data.

glimpse(yose_vft)
#> Observations: 92
#> Variables: 32
#> $ DBHID            <int> 1, 34881, 2, 34882, 3, 34883, 4, 34884, 5, 34...
#> $ PlotName         <chr> "Yosemite Forest Dynamics Plot", "Yosemite Fo...
#> $ PlotID           <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
#> $ Family           <chr> "Pinaceae", "Pinaceae", "Pinaceae", "Pinaceae...
#> $ Genus            <chr> "Pinus", "Pinus", "Abies", "Abies", "Abies", ...
#> $ SpeciesName      <chr> "lambertiana", "lambertiana", "concolor", "co...
#> $ Mnemonic         <chr> "PILA", "PILA", "ABCO", "ABCO", "ABCO", "ABCO...
#> $ Subspecies       <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL...
#> $ SpeciesID        <int> 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
#> $ SubspeciesID     <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL...
#> $ QuadratName      <chr> "A01", "A01", "A01", "A01", "A01", "A01", "A0...
#> $ QuadratID        <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
#> $ PX               <dbl> 2.82, 2.82, 1.06, 1.06, 3.89, 3.89, 5.86, 5.8...
#> $ PY               <dbl> 11.25, 11.25, 3.13, 3.13, 2.17, 2.17, 3.28, 3...
#> $ x                <dbl> 2.45, 2.45, 0.69, 0.69, 3.52, 3.52, 5.49, 5.4...
#> $ y                <dbl> 10.58, 10.58, 2.46, 2.46, 1.50, 1.50, 2.61, 2...
#> $ TreeID           <int> 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, ...
#> $ Tag              <chr> "01-0001", "01-0001", "01-0002", "01-0002", "...
#> $ StemID           <int> 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, ...
#> $ StemNumber       <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
#> $ StemTag          <chr> "01-0001", "01-0001", "01-0002", "01-0002", "...
#> $ PrimaryStem      <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL...
#> $ CensusID         <int> 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, ...
#> $ PlotCensusNumber <int> 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, ...
#> $ DBH              <chr> "149", "152.4", "54.6", "56.1", "5", "5.4", "...
#> $ HOM              <dbl> 1.37, 1.37, 1.37, 1.37, 1.37, 1.37, 1.37, 1.3...
#> $ ExactDate        <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL...
#> $ Date             <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL...
#> $ ListOfTSM        <chr> "v1;A", "v1;A;b2", "v2;A", "v1;A;b2", "v1;A",...
#> $ HighHOM          <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
#> $ LargeStem        <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL...
#> $ Status           <chr> "alive", "alive", "alive", "alive", "alive", ...

First you filter the specific plot you want to produce maps for.

yose_vft1 <- filter(yose_vft, PlotID == 1)
# Using a private data set; and using only one quadrat for a small example
maps <- map_tag(yose_vft1)

Here is the first set of four subquadrats.

maps[1]
#> $A01_1

You can output a .pdf with pdf().

pdf("example-yosemite.pdf", paper = "a4")
maps
#> $A01_1
#> 
#> $A01_2
#> 
#> $A01_3
#> 
#> $A01_4
dev.off()
#> png 
#>   2

What Does map_tag() Do?

Let’s review what has just happened. Although your ViewFullTable may have data of multiple censuses, map_tag() focuses exclusively on the latest census (via PlotNameCensus). map_tag() plots not the status of individual stems but the status of a tree. That is, if in the latest census at least one stem of a tree has status “alive”, then map_tag() plots its status as “alive”; if no stem is “alive”, map_tag() plots its status as “other”. Why “other” and not “dead”? Because stems are classified not simply as “dead” or “alive”: if it is not “alive”, the value of status may be, for example, “dead” for one stem of a particular tree, and “broken below” for another stem of the same tree. Yet map_tree() is designed to plot only one value of status per individual tag. The solution is to group all values that are not “alive” into the single category “other”.

OK, that was the bad news – map_tag() won’t let you change the values of the variable status. The good news is that you can change almost anything else. You will learn how to do that in the rest of this tutorial.

Preparation: Packages and Data

The packages you will use are try, ggplot2 and dplyr. try contains the function map_tag() (after I receive some feedback I will move it to the package forestr). ggplot2 and dplyr provide powerful tools for vizualizing and wrangling data. (These packages are part of the tidyverse (https://www.tidyverse.org/) – a collection of R packages designed for data science. All packages share an underlying philosophy and common APIs.)

# install_github("forestgeo/try")
library(try)
# install.packages("ggplot2")
library(ggplot2)
# install.packages("dplyr")
library(dplyr)

# Print only a few rows of data framed to save time and space
options(dplyr.print_min = 6, dplyr.print_max = 6)

From now on, The data you will use is an example data set from Barro Colorado Island made public in 2012 (see https://repository.si.edu/handle/10088/20925 and ?try::bci12vft_mini).

# Subset of a public ViewFullTable from BCI (source:
# https://repository.si.edu/handle/10088/20925).

# Convert to tibble (modern dataframe) for better printing
bci_vft <- as_tibble(bci12vft_mini)
glimpse(bci_vft)
#> Observations: 4,374
#> Variables: 28
#> $ MeasureID        <int> 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1...
#> $ PlotID           <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
#> $ Plot             <chr> "bci", "bci", "bci", "bci", "bci", "bci", "bc...
#> $ Family           <chr> "Lecythidaceae", "Myristicaceae", "Malvaceae"...
#> $ GenusSpecies     <chr> "Gustavia superba", "Virola surinamensis", "Q...
#> $ Genus            <chr> "Gustavia", "Virola", "Quararibea", "Protium"...
#> $ SpeciesName      <chr> "superba", "surinamensis", "asterolepis", "te...
#> $ SubSpeciesName   <chr> "NULL", "NULL", "NULL", "NULL", "NULL", "NULL...
#> $ SpeciesID        <int> 460, 1075, 871, 828, 108, 828, 871, 871, 1144...
#> $ Mnemonic         <chr> "gustsu", "virosu", "quaras", "protte", "bros...
#> $ QuadratID        <int> 1250, 1250, 1250, 1249, 1249, 1248, 1248, 124...
#> $ QuadratName      <chr> "4924", "4924", "4924", "4923", "4923", "4922...
#> $ x                <dbl> 14.1, 10.5, 13.5, 12.7, 1.9, 3.7, 3.0, 3.0, 1...
#> $ y                <dbl> 8.3, 8.9, 18.3, 9.3, 13.5, 16.5, 12.6, 12.6, ...
#> $ gx               <dbl> 994, 990, 994, 993, 982, 984, 983, 983, 996, ...
#> $ gy               <dbl> 488, 489, 498, 469, 474, 456, 453, 453, 447, ...
#> $ TreeID           <int> 19, 21, 23, 24, 25, 29, 30, 30, 32, 33, 34, 3...
#> $ Tag              <chr> "000002", "000004", "000006", "000007", "0000...
#> $ StemID           <int> 1, 1, 7, 1, 1, 6, 2, 3, 2, 1, 1, 1, 6, 6, 1, ...
#> $ StemTag          <chr> NA, NA, "NULL", NA, NA, "NULL", "NULL", "NULL...
#> $ PrimaryStem      <chr> "main", "main", "main", "main", "main", "main...
#> $ CensusID         <int> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...
#> $ PlotCensusNumber <int> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...
#> $ DBH              <int> 304, 357, 313, 456, 1290, NA, 492, 33, NA, 42...
#> $ HOM              <dbl> 3.00, 1.30, 3.00, 1.30, 5.20, NA, 3.20, 1.30,...
#> $ ExactDate        <date> 2005-10-14, 2005-10-11, 2005-10-14, 2005-10-...
#> $ ListOfTSM        <chr> "B,cylY", "NULL", "B,cylY", "NULL", "B,cylN",...
#> $ Status           <chr> "alive", "alive", "alive", "alive", "alive", ...
bci_vft1 <- filter(bci_vft, PlotID == 1)

Not all data sets may be named appropriately. map_tag() expects some variables to have specific names. If the name of your variables is different than expected, you’ll get an error.

# Replacing a crucial name with a bad name
bci_vft_bad_nms <- rename(bci_vft1, bad_x = x)

# Fails
map_tag(bci_vft_bad_nms)
#> Error: Ensure your data set has these variables (regardles of the case):
#> tag, x, y, status, quadratname, censusid, plotid

Reading the error message will help you identify which variables you need to rename.

bci_vft1_rnm <- dplyr::rename(bci_vft_bad_nms, x = bad_x)
# Using lowercase names for simiplicity
names(bci_vft1_rnm) <- tolower(names(bci_vft1_rnm))

With the techniques you’ll learn here, you can produce maps of an entire data set, or of just a few quadrats. To save space, let’s focus just on (any) one quadrat.

any_quadrat <- sample(unique(bci_vft1_rnm$quadratname), 1)
filter(bci_vft1_rnm, quadratname == any_quadrat)

Customizing Your Maps

This section shows how you can change the default of your maps (see also ?map_tag()). For example, you can customize the plot title, and the points and tags.

maps <- map_tag(bci_vft1_rnm, 
  site_name = "BCI 2012", point_size = 3, point_shape = c(17, 6), tag_size = 5
)
maps[1]
#> $`4915_1`

Customizing The Header

The header can be customized in two ways. One ways it to pass a string to the argument header.

map_tag(bci_vft1_rnm, site_name = "BCI 2012", header = "My header")[1]
#> $`4915_1`

The string can be multi-lined; to insert line brakes, use “\n”.

map_tag(bci_vft1_rnm, site_name = "BCI 2012", 
  header = "Line 1: _________\nLine 2:\nLine 3:....................."
)[1]
#> $`4915_1`

The second way is to use get_header() (see ?get_header()).

your_header <- get_header(
  line1 = "Your header-line 1: _____________________________",
  line2 = "Your header-line 3: _____________________________",
  line3 = "Your header-line 2: _____________________________"
)
map_tag(bci_vft1_rnm, site_name = "BCI 2012", header = your_header)[1]
#> $`4915_1`

Using Pre-Made and Custom Themes

Similarly to how you customized the header, you can also customize the plot theme. By default, theme = get_theme() (see ?get_theme()); but you can either use any pre-made theme (see ?ggplot2::theme_bw) or create a custom theme. This is how you can use a pre-made theme.

# Allow using pre-made themes (e.g. ggplot2::theme_bw()) and building custom
# themes (with ggplot::theme()).
library(ggplot2)

map_tag(bci_vft1_rnm, site_name = "BCI 2012", theme = theme_gray())[1]
#> $`4915_1`

And this is how you can use a custom theme (see ?ggplot2::theme()).

# An extreeme example -- to show that themes are extreemely flexible
your_theme <- ggplot2::theme(
  legend.position = "bottom",
  legend.title = element_blank(),
  legend.text = element_text(size = 8, colour = "red"),
  text = element_text(size = 11, face = "bold.italic", colour = "white"),
  plot.background = element_rect(fill = "black"),
  plot.margin = margin(2, 2, 2, 2, "cm"),
  strip.background = element_rect(fill = "darkgreen"),
  strip.text = element_text(colour = "white"),
  # make grid to dissapear by matching background colour
  panel.background = element_rect(fill = "lightgreen"),
  panel.grid.minor = element_line(colour = "black", linetype = "dotted"),
  panel.grid.major = element_line(colour = "black")
)
map_tag(bci_vft1_rnm, site_name = "BCI 2012", theme = your_theme)[1]
#> $`4915_1`

Extending The Grid Beyond the Plot Limits

Although they shouldn’t, trees sometimes are located beyond the limits of a quadrat. For example, if the side of your quadrats is 20 meters, some trees may plot at, say, x = 20.5, or y = 21. In such cases you may want to extend the plot grid to encompass those odd trees. To extend the grid use the argument shrink. (The example below has no trees beyond the quadrat limits, but hopefully you’ll still understand when to use extend_grid.)

map_tag(bci_vft1_rnm, site_name = "BCI 2012", extend_grid = 0.4)[4]
#> $`4915_4`

Customizing the Dimension of Quadrats and Subquadrats

You can customize the dimension of your quadrats and subquadrats to fit the range of x and y of your data. Let’s examine what the range is for the data you have been using so far.

x_and_y_variables <- select(bci_vft1_rnm, x, y)
lapply(x_and_y_variables, range)
#> $x
#> [1]  0.0 19.9
#> 
#> $y
#> [1]  0.0 19.9

The range is between around 0 and 20. That is why we have been using using the default quadrat dimension of 20 meters (x_q = 20; and y_sq = x_q = 20), and the default subquadrat dimension of 5 meters (x_sq = 5; and y_sq = x_sq = 5).

map_tag(bci_vft1_rnm, site_name = "BCI 2012", 
  x_q = 20, x_sq = 5,
  y_q = 20, y_sq = 5
)[1]
#> $`4915_1`

By default, y_q will be the same as x_q – so you don’t have to provide both (only x_q is mandatory). And the same is true for y_sq and x_sq. With this data – with x and y ranging 0-20 meters – the quadrat and subquadrat dimensions used above are the right ones. But map_tag() won’t complain if you choose different parameters; so you have to be careful not to shoot yourself on the foot.

The following two examples demonstrate the use wrong dimensions. Let’s first use dimensions that are smaller than the range of x and y.

map_tag(bci_vft1_rnm, 
  site_name = "BCI 2012", x_q = 10, x_sq = 2.5, 
  # if not extended, the lines surrounding the map won't plot
  extend_grid = 0.25
)[1]
#> $`4915_1`

And now let’s use dimensions that are larger than the range of the data.

# Using 
map_tag(bci_vft1_rnm, 
  site_name = "BCI 2012", x_q = 100, x_sq = 25, 
)[1]
#> $`4915_1`

This image should flag that you have this kind of problem – by which the range of data is smaller than the dimensions you set for your quadrat and subquadrat. Notice that the points (the actual positions of the trees) are all between x and y ranging 0-20. The tags, however, go beyond 20 because they automatically repel themselves to avoid overlapping.

The Right Dimensions for the Right Data

Had x and y of your data ranged 0-100, then yes – the appropriate quadrat and subquadrat dimensions to use would be 100 and 25.

# Creating new data set with x and y ranging 0-100
bigger <- bci_vft1_rnm
n <- nrow(bigger)
bigger$x <- sample(0:100, n, replace = TRUE)
bigger$y <- sample(0:100, n, replace = TRUE)

map_tag(
  bigger, 
  x_q = 100, x_sq = 25, 
  extend_grid = -1.75
)[1]
#> $`4915_1`

And if x and y of your data range 0-10, then the appropriate quadrat dimension to use would be 10 and 2.5.

# Creating new data set with x and y ranging 0-100
smaller <- bci_vft1_rnm
n <- nrow(smaller)
smaller$x <- sample(0:10, n, replace = TRUE)
smaller$y <- sample(0:10, n, replace = TRUE)

map_tag(smaller, x_q = 10, x_sq = 2.5, extend_grid = 0.25)[1]
#> $`4915_1`

Calling add_subquadrat() Directly

If you want to only calculate the variable subquadrat, you don’t need to use map_tag() – can directly call add_subquadrat().

with_subquadrat <- add_subquadrat(bci_vft1_rnm, x_q = 20, x_sq = 5)
#> Lowering names case
select(
  with_subquadrat, 
  # reorder variables to show first what's new 
  subquadrat, x, y, everything()
)

But if you don’t mind going through a little more trouble, you can also get the subquadrat variable from the data the underlies the maps.

maps <- map_tag(bci_vft1_rnm, x_q = 20, x_sq = 5)
data_list <- purrr::map(maps, "data")
data_combined <- purrr::reduce(data_list, rbind)
select(
  data_combined,
  subquadrat, x, y, everything()
)

Acknowledgements

I thank for ideas and guidance to Suzanne Lao, Stuart Davis, Shameema Jafferjee Esufa, David Kenfack and Anudeep Singh. Andudeep also wrote the algorithm of add_subquadrat() (which I translated from SQL to R).

LS0tDQp0aXRsZTogIk1hcHBpbmcgTGF0ZXN0IFRyZWUtU3RhdHVzIGluIFN1YnF1YWRyYXRzIg0KYXV0aG9yOiAiTWF1cm8gTGVwb3JlIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBybWFya2Rvd246Omh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiA2DQogICAgdGhlbWU6IHVuaXRlZA0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBge3IgcGtnLWhpZGUsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgaW5zdGFsbF9naXRodWIoImZvcmVzdGdlby90cnkiKQ0KbGlicmFyeSh0cnkpDQojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojIFByaW50IG9ubHkgYSBmZXcgcm93cyBvZiBkYXRhIGZyYW1lZCB0byBzYXZlIHRpbWUgYW5kIHNwYWNlDQpvcHRpb25zKGRwbHlyLnByaW50X21pbiA9IDYsIGRwbHlyLnByaW50X21heCA9IDYpDQpgYGANCg0KYGBge3Igc2V0dXAsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgaGFkbGV5J3Mgc2V0dGluZ3MNCnNldC5zZWVkKDEwMTQpDQpvcHRpb25zKGRpZ2l0cyA9IDMpDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgZWNobyA9IFRSVUUsDQogIGNvbW1lbnQgPSAiIz4iLA0KICBjb2xsYXBzZSA9IFRSVUUsDQogIGNhY2hlID0gVFJVRSwNCiAgb3V0LndpZHRoID0gIjk4JSIsDQogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLA0KICBmaWcud2lkdGg9Ny41LCANCiAgZmlnLmFzcCA9IDAuOSwNCiAgZmlnLnNob3cgPSAiaG9sZCINCikNCg0KdXBkYXRlX2dlb21fZGVmYXVsdHMoInRleHQiLCBsaXN0KHNpemUgPSAxNC41KSkNCmBgYA0KDQojIE92ZXJ2aWV3DQoNClRoaXMgdHV0b3JpYWwgd2lsbCBzaG93IHlvdSB5b3UgaG93IHRvIG1hcCB0aGUgbGF0ZXN0IHRyZWUtc3RhdHVzIGluIHN1YnF1YWRyYXRzIHdpdGggYG1hcF90YWcoKWAuDQoNCiMgTW90aXZhdGlvbg0KDQpJbWFnaW5lIHlvdSBuZWVkIHNvbWUgbWFwcyB0byB3b3JrIGluIHRoZSBmaWVsZCBhdCB0aGUgc2l0ZSBZb3NlbWl0ZS4gQWxsIHlvdSBuZWVkIGlzIGEgVmlld0Z1bGxUYWJsZSBhbmQgdGhlIGZ1bmN0aW9uIGBtYXBfdGFnKClgLiBIZXJlIGlzIGEgZ2xpbXBzZSBvZiB5b3VyIGRhdGEuDQoNCmBgYHtyIGhpZGUtc2luaCwgZWNobz1GQUxTRX0NCmZpbHRlcl9maXJzdF9xdWFkcmF0IDwtIGZ1bmN0aW9uKHZmdCkgew0KICBmaXJzdF9xdWFkcmF0IDwtIHVuaXF1ZSh2ZnQkUXVhZHJhdE5hbWUpWzFdDQogIHZmdFt2ZnQkUXVhZHJhdE5hbWUgPT0gZmlyc3RfcXVhZHJhdCwgXQ0KfQ0KeW9zZV92ZnQgPC0geW9zZW1pdGU6OlZpZXdGdWxsVGFibGVfeW9zZW1pdGUgJT4lIA0KICByZW5hbWUoeCA9IFFYLCB5ID0gUVkpICU+JSANCiAgZmlsdGVyX2ZpcnN0X3F1YWRyYXQoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2xpbXBzZSh5b3NlX3ZmdCkNCmBgYA0KDQpGaXJzdCB5b3UgZmlsdGVyIHRoZSBzcGVjaWZpYyBwbG90IHlvdSB3YW50IHRvIHByb2R1Y2UgbWFwcyBmb3IuDQoNCmBgYHtyfQ0KeW9zZV92ZnQxIDwtIGZpbHRlcih5b3NlX3ZmdCwgUGxvdElEID09IDEpDQpgYGANCg0KYGBge3J9DQojIFVzaW5nIGEgcHJpdmF0ZSBkYXRhIHNldDsgYW5kIHVzaW5nIG9ubHkgb25lIHF1YWRyYXQgZm9yIGEgc21hbGwgZXhhbXBsZQ0KbWFwcyA8LSBtYXBfdGFnKHlvc2VfdmZ0MSkNCmBgYA0KDQpIZXJlIGlzIHRoZSBmaXJzdCBzZXQgb2YgZm91ciBzdWJxdWFkcmF0cy4NCg0KYGBge3IgeW9zZS1wcmludC1zY3JlZW59DQptYXBzWzFdDQpgYGANCg0KWW91IGNhbiBvdXRwdXQgYSAucGRmIHdpdGggYHBkZigpYC4NCg0KYGBge3IgeW9zZS1wcmludC1wZGZ9DQpwZGYoImV4YW1wbGUteW9zZW1pdGUucGRmIiwgcGFwZXIgPSAiYTQiKQ0KbWFwcw0KZGV2Lm9mZigpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiZXhhbXBsZS15b3NlbWl0ZS5wZGYiKQ0KYGBgDQoNCiMjIFdoYXQgRG9lcyBgbWFwX3RhZygpYCBEbz8NCg0KTGV0J3MgcmV2aWV3IHdoYXQgaGFzIGp1c3QgaGFwcGVuZWQuIEFsdGhvdWdoIHlvdXIgVmlld0Z1bGxUYWJsZSBtYXkgaGF2ZSBkYXRhIG9mIG11bHRpcGxlIGNlbnN1c2VzLCBgbWFwX3RhZygpYCBmb2N1c2VzIGV4Y2x1c2l2ZWx5IG9uIHRoZSBsYXRlc3QgY2Vuc3VzICh2aWEgUGxvdE5hbWVDZW5zdXMpLiBgbWFwX3RhZygpYCBwbG90cyBub3QgdGhlIHN0YXR1cyBvZiBpbmRpdmlkdWFsIHN0ZW1zIGJ1dCB0aGUgc3RhdHVzIG9mIGEgdHJlZS4gVGhhdCBpcywgaWYgaW4gdGhlIGxhdGVzdCBjZW5zdXMgYXQgbGVhc3Qgb25lIHN0ZW0gb2YgYSB0cmVlIGhhcyBzdGF0dXMgImFsaXZlIiwgdGhlbiBgbWFwX3RhZygpYCBwbG90cyBpdHMgc3RhdHVzIGFzICJhbGl2ZSI7IGlmIG5vIHN0ZW0gaXMgImFsaXZlIiwgYG1hcF90YWcoKWAgcGxvdHMgaXRzIHN0YXR1cyBhcyAib3RoZXIiLiBXaHkgIm90aGVyIiBhbmQgbm90ICJkZWFkIj8gQmVjYXVzZSBzdGVtcyBhcmUgY2xhc3NpZmllZCBub3Qgc2ltcGx5IGFzICJkZWFkIiBvciAiYWxpdmUiOiBpZiBpdCBpcyBub3QgImFsaXZlIiwgdGhlIHZhbHVlIG9mIGBzdGF0dXNgIG1heSBiZSwgZm9yIGV4YW1wbGUsICJkZWFkIiBmb3Igb25lIHN0ZW0gb2YgYSBwYXJ0aWN1bGFyIHRyZWUsIGFuZCAiYnJva2VuIGJlbG93IiBmb3IgYW5vdGhlciBzdGVtIG9mIHRoZSBzYW1lIHRyZWUuIFlldCBgbWFwX3RyZWUoKWAgaXMgZGVzaWduZWQgdG8gcGxvdCBvbmx5IG9uZSB2YWx1ZSBvZiBzdGF0dXMgcGVyIGluZGl2aWR1YWwgdGFnLiBUaGUgc29sdXRpb24gaXMgdG8gZ3JvdXAgYWxsIHZhbHVlcyB0aGF0IGFyZSBub3QgImFsaXZlIiBpbnRvIHRoZSBzaW5nbGUgY2F0ZWdvcnkgIm90aGVyIi4NCg0KT0ssIHRoYXQgd2FzIHRoZSBiYWQgbmV3cyAtLSBgbWFwX3RhZygpYCB3b24ndCBsZXQgeW91IGNoYW5nZSB0aGUgdmFsdWVzIG9mIHRoZSB2YXJpYWJsZSBgc3RhdHVzYC4gVGhlIGdvb2QgbmV3cyBpcyB0aGF0IHlvdSBjYW4gY2hhbmdlIGFsbW9zdCBhbnl0aGluZyBlbHNlLiBZb3Ugd2lsbCBsZWFybiBob3cgdG8gZG8gdGhhdCBpbiB0aGUgcmVzdCBvZiB0aGlzIHR1dG9yaWFsLg0KDQojIFByZXBhcmF0aW9uOiBQYWNrYWdlcyBhbmQgRGF0YQ0KDQpUaGUgcGFja2FnZXMgeW91IHdpbGwgdXNlIGFyZSBfX3RyeV9fLCBfX2dncGxvdDJfXyBhbmQgX19kcGx5cl9fLiBfX3RyeV9fIGNvbnRhaW5zIHRoZSBmdW5jdGlvbiBgbWFwX3RhZygpYCAoYWZ0ZXIgSSByZWNlaXZlIHNvbWUgZmVlZGJhY2sgSSB3aWxsIG1vdmUgaXQgdG8gdGhlIHBhY2thZ2UgX19mb3Jlc3RyX18pLiBfX2dncGxvdDJfXyBhbmQgX19kcGx5cl9fIHByb3ZpZGUgcG93ZXJmdWwgdG9vbHMgZm9yIHZpenVhbGl6aW5nIGFuZCB3cmFuZ2xpbmcgZGF0YS4gKFRoZXNlIHBhY2thZ2VzIGFyZSBwYXJ0IG9mIHRoZSB0aWR5dmVyc2UgKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKSAtLSBhIGNvbGxlY3Rpb24gb2YgUiBwYWNrYWdlcyBkZXNpZ25lZCBmb3IgZGF0YSBzY2llbmNlLiBBbGwgcGFja2FnZXMgc2hhcmUgYW4gdW5kZXJseWluZyBwaGlsb3NvcGh5IGFuZCBjb21tb24gQVBJcy4pDQoNCmBgYHtyIHNoby1jaHVuay10aGF0LXdhcy1oaWRkZW4tYWJvdmV9DQo8PHBrZy1oaWRlPj4NCmBgYA0KDQpGcm9tIG5vdyBvbiwgVGhlIGRhdGEgeW91IHdpbGwgdXNlIGlzIGFuIGV4YW1wbGUgZGF0YSBzZXQgZnJvbSBCYXJybyBDb2xvcmFkbyBJc2xhbmQgbWFkZSBwdWJsaWMgaW4gMjAxMiAoc2VlIGh0dHBzOi8vcmVwb3NpdG9yeS5zaS5lZHUvaGFuZGxlLzEwMDg4LzIwOTI1IGFuZCBbYD90cnk6OmJjaTEydmZ0X21pbmlgXShodHRwczovL2ZvcmVzdGdlby5naXRodWIuaW8vdHJ5L3JlZmVyZW5jZS9iY2kxMnZmdF9taW5pLmh0bWwpKS4NCg0KYGBge3IgZGF0YS12dGZ9DQojIFN1YnNldCBvZiBhIHB1YmxpYyBWaWV3RnVsbFRhYmxlIGZyb20gQkNJIChzb3VyY2U6DQojIGh0dHBzOi8vcmVwb3NpdG9yeS5zaS5lZHUvaGFuZGxlLzEwMDg4LzIwOTI1KS4NCg0KIyBDb252ZXJ0IHRvIHRpYmJsZSAobW9kZXJuIGRhdGFmcmFtZSkgZm9yIGJldHRlciBwcmludGluZw0KYmNpX3ZmdCA8LSBhc190aWJibGUoYmNpMTJ2ZnRfbWluaSkNCmdsaW1wc2UoYmNpX3ZmdCkNCmBgYA0KDQpgYGB7cn0NCmJjaV92ZnQxIDwtIGZpbHRlcihiY2lfdmZ0LCBQbG90SUQgPT0gMSkNCmBgYA0KDQpOb3QgYWxsIGRhdGEgc2V0cyBtYXkgYmUgbmFtZWQgYXBwcm9wcmlhdGVseS4gYG1hcF90YWcoKWAgZXhwZWN0cyBzb21lIHZhcmlhYmxlcyB0byBoYXZlIHNwZWNpZmljIG5hbWVzLiBJZiB0aGUgbmFtZSBvZiB5b3VyIHZhcmlhYmxlcyBpcyBkaWZmZXJlbnQgdGhhbiBleHBlY3RlZCwgeW91J2xsIGdldCBhbiBlcnJvci4NCg0KYGBge3IgcmVuYW1lLXZhcmlhYmxlcywgZXJyb3I9VFJVRX0NCiMgUmVwbGFjaW5nIGEgY3J1Y2lhbCBuYW1lIHdpdGggYSBiYWQgbmFtZQ0KYmNpX3ZmdF9iYWRfbm1zIDwtIHJlbmFtZShiY2lfdmZ0MSwgYmFkX3ggPSB4KQ0KDQojIEZhaWxzDQptYXBfdGFnKGJjaV92ZnRfYmFkX25tcykNCmBgYA0KDQpSZWFkaW5nIHRoZSBlcnJvciBtZXNzYWdlIHdpbGwgaGVscCB5b3UgaWRlbnRpZnkgd2hpY2ggdmFyaWFibGVzIHlvdSBuZWVkIHRvIHJlbmFtZS4NCg0KYGBge3IgZml4LW5hbWVzfQ0KYmNpX3ZmdDFfcm5tIDwtIGRwbHlyOjpyZW5hbWUoYmNpX3ZmdF9iYWRfbm1zLCB4ID0gYmFkX3gpDQojIFVzaW5nIGxvd2VyY2FzZSBuYW1lcyBmb3Igc2ltaXBsaWNpdHkNCm5hbWVzKGJjaV92ZnQxX3JubSkgPC0gdG9sb3dlcihuYW1lcyhiY2lfdmZ0MV9ybm0pKQ0KYGBgDQoNCldpdGggdGhlIHRlY2huaXF1ZXMgeW91J2xsIGxlYXJuIGhlcmUsIHlvdSBjYW4gcHJvZHVjZSBtYXBzIG9mIGFuIGVudGlyZSBkYXRhIHNldCwgb3Igb2YganVzdCBhIGZldyBxdWFkcmF0cy4gVG8gc2F2ZSBzcGFjZSwgbGV0J3MgZm9jdXMganVzdCBvbiAoYW55KSBvbmUgcXVhZHJhdC4NCg0KYGBge3IgZmlsdGVyLWJjaX0NCmFueV9xdWFkcmF0IDwtIHNhbXBsZSh1bmlxdWUoYmNpX3ZmdDFfcm5tJHF1YWRyYXRuYW1lKSwgMSkNCmZpbHRlcihiY2lfdmZ0MV9ybm0sIHF1YWRyYXRuYW1lID09IGFueV9xdWFkcmF0KQ0KYGBgDQoNCiMgQ3VzdG9taXppbmcgWW91ciBNYXBzDQoNClRoaXMgc2VjdGlvbiBzaG93cyBob3cgeW91IGNhbiBjaGFuZ2UgdGhlIGRlZmF1bHQgb2YgeW91ciBtYXBzIChzZWUgYWxzbyBgP21hcF90YWcoKWApLiBGb3IgZXhhbXBsZSwgeW91IGNhbiBjdXN0b21pemUgdGhlIHBsb3QgdGl0bGUsIGFuZCB0aGUgcG9pbnRzIGFuZCB0YWdzLiANCg0KYGBge3IgY3VzdG9tLXRpdGxlLXBvaW50LXNoYXBlfQ0KbWFwcyA8LSBtYXBfdGFnKGJjaV92ZnQxX3JubSwgDQogIHNpdGVfbmFtZSA9ICJCQ0kgMjAxMiIsIHBvaW50X3NpemUgPSAzLCBwb2ludF9zaGFwZSA9IGMoMTcsIDYpLCB0YWdfc2l6ZSA9IDUNCikNCm1hcHNbMV0NCmBgYA0KDQojIyBDdXN0b21pemluZyBUaGUgSGVhZGVyDQoNClRoZSBoZWFkZXIgY2FuIGJlIGN1c3RvbWl6ZWQgaW4gdHdvIHdheXMuIE9uZSB3YXlzIGl0IHRvIHBhc3MgYSBzdHJpbmcgdG8gdGhlIGFyZ3VtZW50IGBoZWFkZXJgLg0KDQpgYGB7ciBjdXN0b20taGVhZGVyfQ0KbWFwX3RhZyhiY2lfdmZ0MV9ybm0sIHNpdGVfbmFtZSA9ICJCQ0kgMjAxMiIsIGhlYWRlciA9ICJNeSBoZWFkZXIiKVsxXQ0KYGBgDQoNClRoZSBzdHJpbmcgY2FuIGJlIG11bHRpLWxpbmVkOyB0byBpbnNlcnQgbGluZSBicmFrZXMsIHVzZSAiXFxuIi4NCg0KYGBge3IgaGVhZGVyLWJhc2ljfQ0KbWFwX3RhZyhiY2lfdmZ0MV9ybm0sIHNpdGVfbmFtZSA9ICJCQ0kgMjAxMiIsIA0KICBoZWFkZXIgPSAiTGluZSAxOiBfX19fX19fX19cbkxpbmUgMjpcbkxpbmUgMzouLi4uLi4uLi4uLi4uLi4uLi4uLi4iDQopWzFdDQpgYGANCg0KVGhlIHNlY29uZCB3YXkgaXMgdG8gdXNlIGBnZXRfaGVhZGVyKClgIChzZWUgYD9nZXRfaGVhZGVyKClgKS4NCg0KYGBge3IgaGVhZGVyLXZpYS1oZWxwZXJ9DQp5b3VyX2hlYWRlciA8LSBnZXRfaGVhZGVyKA0KICBsaW5lMSA9ICJZb3VyIGhlYWRlci1saW5lIDE6IF9fX19fX19fX19fX19fX19fX19fX19fX19fX19fIiwNCiAgbGluZTIgPSAiWW91ciBoZWFkZXItbGluZSAzOiBfX19fX19fX19fX19fX19fX19fX19fX19fX19fXyIsDQogIGxpbmUzID0gIllvdXIgaGVhZGVyLWxpbmUgMjogX19fX19fX19fX19fX19fX19fX19fX19fX19fX18iDQopDQptYXBfdGFnKGJjaV92ZnQxX3JubSwgc2l0ZV9uYW1lID0gIkJDSSAyMDEyIiwgaGVhZGVyID0geW91cl9oZWFkZXIpWzFdDQpgYGANCg0KIyMgVXNpbmcgUHJlLU1hZGUgYW5kIEN1c3RvbSBUaGVtZXMNCg0KU2ltaWxhcmx5IHRvIGhvdyB5b3UgY3VzdG9taXplZCB0aGUgaGVhZGVyLCB5b3UgY2FuIGFsc28gY3VzdG9taXplIHRoZSBwbG90IHRoZW1lLiBCeSBkZWZhdWx0LCBgdGhlbWUgPSBnZXRfdGhlbWUoKWAgKHNlZSBgP2dldF90aGVtZSgpYCk7IGJ1dCB5b3UgY2FuIGVpdGhlciB1c2UgYW55IHByZS1tYWRlIHRoZW1lIChzZWUgYD9nZ3Bsb3QyOjp0aGVtZV9id2ApIG9yIGNyZWF0ZSBhIGN1c3RvbSB0aGVtZS4gVGhpcyBpcyBob3cgeW91IGNhbiB1c2UgYSBwcmUtbWFkZSB0aGVtZS4NCg0KYGBge3IgcHJlLW1hZGUtdGhlbWV9DQojIEFsbG93IHVzaW5nIHByZS1tYWRlIHRoZW1lcyAoZS5nLiBnZ3Bsb3QyOjp0aGVtZV9idygpKSBhbmQgYnVpbGRpbmcgY3VzdG9tDQojIHRoZW1lcyAod2l0aCBnZ3Bsb3Q6OnRoZW1lKCkpLg0KbGlicmFyeShnZ3Bsb3QyKQ0KDQptYXBfdGFnKGJjaV92ZnQxX3JubSwgc2l0ZV9uYW1lID0gIkJDSSAyMDEyIiwgdGhlbWUgPSB0aGVtZV9ncmF5KCkpWzFdDQpgYGANCg0KQW5kIHRoaXMgaXMgaG93IHlvdSBjYW4gdXNlIGEgY3VzdG9tIHRoZW1lIChzZWUgYD9nZ3Bsb3QyOjp0aGVtZSgpYCkuDQoNCmBgYHtyIGN1c3RvbS10aGVtZX0NCiMgQW4gZXh0cmVlbWUgZXhhbXBsZSAtLSB0byBzaG93IHRoYXQgdGhlbWVzIGFyZSBleHRyZWVtZWx5IGZsZXhpYmxlDQp5b3VyX3RoZW1lIDwtIGdncGxvdDI6OnRoZW1lKA0KICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gInJlZCIpLA0KICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkLml0YWxpYyIsIGNvbG91ciA9ICJ3aGl0ZSIpLA0KICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJibGFjayIpLA0KICBwbG90Lm1hcmdpbiA9IG1hcmdpbigyLCAyLCAyLCAyLCAiY20iKSwNCiAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImRhcmtncmVlbiIpLA0KICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJ3aGl0ZSIpLA0KICAjIG1ha2UgZ3JpZCB0byBkaXNzYXBlYXIgYnkgbWF0Y2hpbmcgYmFja2dyb3VuZCBjb2xvdXINCiAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImxpZ2h0Z3JlZW4iKSwNCiAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkb3R0ZWQiKSwNCiAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKQ0KKQ0KbWFwX3RhZyhiY2lfdmZ0MV9ybm0sIHNpdGVfbmFtZSA9ICJCQ0kgMjAxMiIsIHRoZW1lID0geW91cl90aGVtZSlbMV0NCmBgYA0KDQojIyBFeHRlbmRpbmcgVGhlIEdyaWQgQmV5b25kIHRoZSBQbG90IExpbWl0cw0KDQpBbHRob3VnaCB0aGV5IHNob3VsZG4ndCwgdHJlZXMgc29tZXRpbWVzIGFyZSBsb2NhdGVkIGJleW9uZCB0aGUgbGltaXRzIG9mIGEgcXVhZHJhdC4gRm9yIGV4YW1wbGUsIGlmIHRoZSBzaWRlIG9mIHlvdXIgcXVhZHJhdHMgaXMgMjAgbWV0ZXJzLCBzb21lIHRyZWVzIG1heSBwbG90IGF0LCBzYXksIGB4ID0gMjAuNWAsIG9yIGB5ID0gMjFgLiBJbiBzdWNoIGNhc2VzIHlvdSBtYXkgd2FudCB0byBleHRlbmQgdGhlIHBsb3QgZ3JpZCB0byBlbmNvbXBhc3MgdGhvc2Ugb2RkIHRyZWVzLiBUbyBleHRlbmQgdGhlIGdyaWQgdXNlIHRoZSBhcmd1bWVudCBgc2hyaW5rYC4gKFRoZSBleGFtcGxlIGJlbG93IGhhcyBubyB0cmVlcyBiZXlvbmQgdGhlIHF1YWRyYXQgbGltaXRzLCBidXQgaG9wZWZ1bGx5IHlvdSdsbCBzdGlsbCB1bmRlcnN0YW5kIHdoZW4gdG8gdXNlIGBleHRlbmRfZ3JpZGAuKQ0KDQpgYGB7cn0NCm1hcF90YWcoYmNpX3ZmdDFfcm5tLCBzaXRlX25hbWUgPSAiQkNJIDIwMTIiLCBleHRlbmRfZ3JpZCA9IDAuNClbNF0NCmBgYA0KDQojIyBDdXN0b21pemluZyB0aGUgRGltZW5zaW9uIG9mIFF1YWRyYXRzIGFuZCBTdWJxdWFkcmF0cw0KDQpZb3UgY2FuIGN1c3RvbWl6ZSB0aGUgZGltZW5zaW9uIG9mIHlvdXIgcXVhZHJhdHMgYW5kIHN1YnF1YWRyYXRzIHRvIGZpdCB0aGUgcmFuZ2Ugb2YgYHhgIGFuZCBgeWAgb2YgeW91ciBkYXRhLiBMZXQncyBleGFtaW5lIHdoYXQgdGhlIHJhbmdlIGlzIGZvciB0aGUgZGF0YSB5b3UgaGF2ZSBiZWVuIHVzaW5nIHNvIGZhci4NCg0KYGBge3J9DQp4X2FuZF95X3ZhcmlhYmxlcyA8LSBzZWxlY3QoYmNpX3ZmdDFfcm5tLCB4LCB5KQ0KbGFwcGx5KHhfYW5kX3lfdmFyaWFibGVzLCByYW5nZSkNCmBgYA0KDQpUaGUgcmFuZ2UgaXMgYmV0d2VlbiBhcm91bmQgMCBhbmQgMjAuIFRoYXQgaXMgd2h5IHdlIGhhdmUgYmVlbiB1c2luZyB1c2luZyB0aGUgZGVmYXVsdCBxdWFkcmF0IGRpbWVuc2lvbiBvZiAyMCBtZXRlcnMgKGB4X3EgPSAyMGA7IGFuZCBgeV9zcSA9IHhfcSA9IDIwYCksIGFuZCB0aGUgZGVmYXVsdCBzdWJxdWFkcmF0IGRpbWVuc2lvbiBvZiA1IG1ldGVycyAoYHhfc3EgPSA1YDsgYW5kIGB5X3NxID0geF9zcSA9IDVgKS4NCg0KYGBge3J9DQptYXBfdGFnKGJjaV92ZnQxX3JubSwgc2l0ZV9uYW1lID0gIkJDSSAyMDEyIiwgDQogIHhfcSA9IDIwLCB4X3NxID0gNSwNCiAgeV9xID0gMjAsIHlfc3EgPSA1DQopWzFdDQpgYGANCg0KQnkgZGVmYXVsdCwgYHlfcWAgd2lsbCBiZSB0aGUgc2FtZSBhcyBgeF9xYCAtLSBzbyB5b3UgZG9uJ3QgaGF2ZSB0byBwcm92aWRlIGJvdGggKG9ubHkgYHhfcWAgaXMgbWFuZGF0b3J5KS4gQW5kIHRoZSBzYW1lIGlzIHRydWUgZm9yIGB5X3NxYCBhbmQgYHhfc3FgLiBXaXRoIHRoaXMgZGF0YSAtLSB3aXRoIGB4YCBhbmQgYHlgIHJhbmdpbmcgMC0yMCBtZXRlcnMgLS0gdGhlIHF1YWRyYXQgYW5kIHN1YnF1YWRyYXQgZGltZW5zaW9ucyB1c2VkIGFib3ZlIGFyZSB0aGUgcmlnaHQgb25lcy4gQnV0IGBtYXBfdGFnKClgIHdvbid0IGNvbXBsYWluIGlmIHlvdSBjaG9vc2UgZGlmZmVyZW50IHBhcmFtZXRlcnM7IHNvIHlvdSBoYXZlIHRvIGJlIGNhcmVmdWwgbm90IHRvIHNob290IHlvdXJzZWxmIG9uIHRoZSBmb290Lg0KDQpUaGUgZm9sbG93aW5nIHR3byBleGFtcGxlcyBkZW1vbnN0cmF0ZSB0aGUgdXNlIHdyb25nIGRpbWVuc2lvbnMuIExldCdzIGZpcnN0IHVzZSBkaW1lbnNpb25zIHRoYXQgYXJlIHNtYWxsZXIgdGhhbiB0aGUgcmFuZ2Ugb2YgYHhgIGFuZCBgeWAuDQoNCmBgYHtyfQ0KbWFwX3RhZyhiY2lfdmZ0MV9ybm0sIA0KICBzaXRlX25hbWUgPSAiQkNJIDIwMTIiLCB4X3EgPSAxMCwgeF9zcSA9IDIuNSwgDQogICMgaWYgbm90IGV4dGVuZGVkLCB0aGUgbGluZXMgc3Vycm91bmRpbmcgdGhlIG1hcCB3b24ndCBwbG90DQogIGV4dGVuZF9ncmlkID0gMC4yNQ0KKVsxXQ0KYGBgDQoNCkFuZCBub3cgbGV0J3MgdXNlIGRpbWVuc2lvbnMgdGhhdCBhcmUgbGFyZ2VyIHRoYW4gdGhlIHJhbmdlIG9mIHRoZSBkYXRhLg0KDQpgYGB7cn0NCiMgVXNpbmcgDQptYXBfdGFnKGJjaV92ZnQxX3JubSwgDQogIHNpdGVfbmFtZSA9ICJCQ0kgMjAxMiIsIHhfcSA9IDEwMCwgeF9zcSA9IDI1LCANCilbMV0NCmBgYA0KDQpUaGlzIGltYWdlIHNob3VsZCBmbGFnIHRoYXQgeW91IGhhdmUgdGhpcyBraW5kIG9mIHByb2JsZW0gLS0gYnkgd2hpY2ggdGhlIHJhbmdlIG9mIGRhdGEgaXMgc21hbGxlciB0aGFuIHRoZSBkaW1lbnNpb25zIHlvdSBzZXQgZm9yIHlvdXIgcXVhZHJhdCBhbmQgc3VicXVhZHJhdC4gTm90aWNlIHRoYXQgdGhlIHBvaW50cyAodGhlIGFjdHVhbCBwb3NpdGlvbnMgb2YgdGhlIHRyZWVzKSBhcmUgYWxsIGJldHdlZW4gYHhgIGFuZCBgeWAgcmFuZ2luZyAwLTIwLiBUaGUgdGFncywgaG93ZXZlciwgZ28gYmV5b25kIDIwIGJlY2F1c2UgdGhleSBhdXRvbWF0aWNhbGx5IHJlcGVsIHRoZW1zZWx2ZXMgdG8gYXZvaWQgb3ZlcmxhcHBpbmcuDQoNCiMjIFRoZSBSaWdodCBEaW1lbnNpb25zIGZvciB0aGUgUmlnaHQgRGF0YQ0KDQpIYWQgYHhgIGFuZCBgeWAgb2YgeW91ciBkYXRhIHJhbmdlZCAwLTEwMCwgdGhlbiB5ZXMgLS0gdGhlIGFwcHJvcHJpYXRlIHF1YWRyYXQgYW5kIHN1YnF1YWRyYXQgZGltZW5zaW9ucyB0byB1c2Ugd291bGQgYmUgMTAwIGFuZCAyNS4NCg0KYGBge3IgYmlnZ2VyfQ0KIyBDcmVhdGluZyBuZXcgZGF0YSBzZXQgd2l0aCB4IGFuZCB5IHJhbmdpbmcgMC0xMDANCmJpZ2dlciA8LSBiY2lfdmZ0MV9ybm0NCm4gPC0gbnJvdyhiaWdnZXIpDQpiaWdnZXIkeCA8LSBzYW1wbGUoMDoxMDAsIG4sIHJlcGxhY2UgPSBUUlVFKQ0KYmlnZ2VyJHkgPC0gc2FtcGxlKDA6MTAwLCBuLCByZXBsYWNlID0gVFJVRSkNCg0KbWFwX3RhZygNCiAgYmlnZ2VyLCANCiAgeF9xID0gMTAwLCB4X3NxID0gMjUsIA0KICBleHRlbmRfZ3JpZCA9IC0xLjc1DQopWzFdDQpgYGANCg0KQW5kIGlmIGB4YCBhbmQgYHlgIG9mIHlvdXIgZGF0YSByYW5nZSAwLTEwLCB0aGVuIHRoZSBhcHByb3ByaWF0ZSBxdWFkcmF0IGRpbWVuc2lvbiB0byB1c2Ugd291bGQgYmUgMTAgYW5kIDIuNS4NCg0KYGBge3Igc21hbGxlcn0NCiMgQ3JlYXRpbmcgbmV3IGRhdGEgc2V0IHdpdGggeCBhbmQgeSByYW5naW5nIDAtMTAwDQpzbWFsbGVyIDwtIGJjaV92ZnQxX3JubQ0KbiA8LSBucm93KHNtYWxsZXIpDQpzbWFsbGVyJHggPC0gc2FtcGxlKDA6MTAsIG4sIHJlcGxhY2UgPSBUUlVFKQ0Kc21hbGxlciR5IDwtIHNhbXBsZSgwOjEwLCBuLCByZXBsYWNlID0gVFJVRSkNCg0KbWFwX3RhZyhzbWFsbGVyLCB4X3EgPSAxMCwgeF9zcSA9IDIuNSwgZXh0ZW5kX2dyaWQgPSAwLjI1KVsxXQ0KYGBgDQoNCiMgQ2FsbGluZyBgYWRkX3N1YnF1YWRyYXQoKWAgRGlyZWN0bHkNCg0KSWYgeW91IHdhbnQgdG8gb25seSBjYWxjdWxhdGUgdGhlIHZhcmlhYmxlIGBzdWJxdWFkcmF0YCwgeW91IGRvbid0IG5lZWQgdG8gdXNlIGBtYXBfdGFnKClgIC0tIGNhbiBkaXJlY3RseSBjYWxsIGBhZGRfc3VicXVhZHJhdCgpYC4NCg0KYGBge3J9DQp3aXRoX3N1YnF1YWRyYXQgPC0gYWRkX3N1YnF1YWRyYXQoYmNpX3ZmdDFfcm5tLCB4X3EgPSAyMCwgeF9zcSA9IDUpDQpzZWxlY3QoDQogIHdpdGhfc3VicXVhZHJhdCwgDQogICMgcmVvcmRlciB2YXJpYWJsZXMgdG8gc2hvdyBmaXJzdCB3aGF0J3MgbmV3IA0KICBzdWJxdWFkcmF0LCB4LCB5LCBldmVyeXRoaW5nKCkNCikNCmBgYA0KDQpCdXQgaWYgeW91IGRvbid0IG1pbmQgZ29pbmcgdGhyb3VnaCBhIGxpdHRsZSBtb3JlIHRyb3VibGUsIHlvdSBjYW4gYWxzbyBnZXQgdGhlIGBzdWJxdWFkcmF0YCB2YXJpYWJsZSBmcm9tIHRoZSBkYXRhIHRoZSB1bmRlcmxpZXMgdGhlIG1hcHMuDQoNCmBgYHtyfQ0KbWFwcyA8LSBtYXBfdGFnKGJjaV92ZnQxX3JubSwgeF9xID0gMjAsIHhfc3EgPSA1KQ0KZGF0YV9saXN0IDwtIHB1cnJyOjptYXAobWFwcywgImRhdGEiKQ0KZGF0YV9jb21iaW5lZCA8LSBwdXJycjo6cmVkdWNlKGRhdGFfbGlzdCwgcmJpbmQpDQpzZWxlY3QoDQogIGRhdGFfY29tYmluZWQsDQogIHN1YnF1YWRyYXQsIHgsIHksIGV2ZXJ5dGhpbmcoKQ0KKQ0KYGBgDQoNCiMgQWNrbm93bGVkZ2VtZW50cw0KDQpJIHRoYW5rIGZvciBpZGVhcyBhbmQgZ3VpZGFuY2UgdG8gU3V6YW5uZSBMYW8sIFN0dWFydCBEYXZpcywgU2hhbWVlbWEgSmFmZmVyamVlIEVzdWZhLCBEYXZpZCBLZW5mYWNrIGFuZCBBbnVkZWVwIFNpbmdoLiBBbmR1ZGVlcCBhbHNvIHdyb3RlIHRoZSBhbGdvcml0aG0gb2YgYGFkZF9zdWJxdWFkcmF0KClgICh3aGljaCBJIHRyYW5zbGF0ZWQgZnJvbSBTUUwgdG8gUikuDQo=