Purpose

Introduce and display examples of how to use R Markdown to bring together code and text in one document to share your work.

Global code and functions

# Load packages
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.3.6      ✔ purrr   0.3.5 
## ✔ tibble  3.1.8      ✔ dplyr   1.0.10
## ✔ tidyr   1.2.1      ✔ stringr 1.4.1 
## ✔ readr   2.1.3      ✔ forcats 0.5.2 
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
library(knitr)
library(here)
## here() starts at C:/Repositories/05_EMRR_Org/RMarkdown-GitHub-tutorial2022

Import Data

Let’s import some example fish count data collected by YBFMP.

df_monthly <- read_csv(here("materials/Monthly_Catch_CPUE.csv"))
## Rows: 557 Columns: 7
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr  (1): Gear
## dbl  (5): year, month, effort, mo.count, CPUE
## date (1): Date
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

This data contains monthly fish counts and CPUE for three different gear types (FKTR, RSTR, BSEIN) from 1998 to 2020.


A word about working directories and .Rmd files:
The default working directory when an .Rmd file is knitted is the .Rmd document directory. This can get confusing when you are using an R project, in which the default working directory is the root directory of the R project. So it’s possible to have different working directories when you are running code from code blocks in an .Rmd file versus when you knit the .Rmd file. A useful R package to help with working directories and file paths is the here R package. You can see that we’re using it in the code chunk above to clearly define the file path for the fish data. The here::here() function points to the root directory of an R project regardless of what the current working directory is. For example:

# Current working directory
getwd()
## [1] "C:/Repositories/05_EMRR_Org/RMarkdown-GitHub-tutorial2022/materials"
# Working directory defined by here::here()
here()
## [1] "C:/Repositories/05_EMRR_Org/RMarkdown-GitHub-tutorial2022"

You can continue the file path within the here::here() function as a character string, which appends this string to the the root directory of the R project. For example, here is the file path to the fish data imported above:

# Define file path to fish data relative to the root directory using here::here()
here("materials/Monthly_Catch_CPUE.csv")
## [1] "C:/Repositories/05_EMRR_Org/RMarkdown-GitHub-tutorial2022/materials/Monthly_Catch_CPUE.csv"

Using Code Chunks

Let’s practice using code chunks to create and print a plot displaying monthly CPUE by gear type.

plt_cpue_month <- df_monthly %>% 
  ggplot(aes(x = Date, y = CPUE)) +
  geom_col() +
  facet_wrap(vars(Gear), ncol = 1, scales = "free_y") + 
  theme_bw()

plt_cpue_month

Code Chunk Options

Now that we’ve seen how to use code chunks in an .Rmd document, lets take a look at some commonly-used code chunk options that configure if and how the code chunk and its results are displayed.

message, warning

Notice when we imported the fish count data above, there is a message printed in the output. If we don’t want this message to display, we can set message = FALSE for the code chunk.

df_monthly <- read_csv(here("materials/Monthly_Catch_CPUE.csv"))

Now the message doesn’t display, but the code was still executed. Same applies to warnings.

eval

If you want the code to appear in the output file, but for the code to NOT run when the Rmd file is knitted, set eval = FALSE as a code chunk option. For example, you may want to show the code you used to summarize the count and CPUE data as annual totals in a new data frame df_annual and plot the CPUE results saving it as plt_cpue_yr, but not actually run the code when you knit the file.

# Summarize count and CPUE as annual totals
df_annual <- df_monthly %>% 
  group_by(year, Gear) %>% 
  summarize(across(c(mo.count, CPUE), sum), .groups = "drop") %>%
  rename(
    Year = year,
    Count = mo.count
  )

# Plot annual CPUE
plt_cpue_yr <- df_annual %>% 
  ggplot(aes(x = Year, y = CPUE)) +
  geom_col() +
  facet_wrap(vars(Gear), ncol = 1, scales = "free_y") + 
  theme_bw()

plt_cpue_yr

Now if you were to try to print df_annual and plt_cpue_yr, you’ll get an error since the code wasn’t executed when the document was knitted.

# Show glimpse of df_annual
glimpse(df_annual)
## Error in glimpse(df_annual): object 'df_annual' not found
# Print plot of annual CPUE
plt_cpue_yr
## Error in eval(expr, envir, enclos): object 'plt_cpue_yr' not found

include

If you want the code to be executed when the Rmd file is knitted, but for the code and results to NOT appear in the output file, set include = FALSE as a code chunk option. For example, let’s summarize and plot the CPUE data using the same code shown above, but this time we’ll use include = FALSE so the code and results won’t appear in the output document.

Even though the code and results aren’t displayed in the document, the df_annual data frame and plt_cpue_yr plot object are available to be used within the Rmd file.

# Show glimpse of df_annual
glimpse(df_annual)
## Rows: 67
## Columns: 4
## $ Year  <dbl> 1998, 1998, 1999, 1999, 2000, 2000, 2000, 2001, 2001, 2001, 2002…
## $ Gear  <chr> "BSEIN", "RSTR", "BSEIN", "FKTR", "BSEIN", "FKTR", "RSTR", "BSEI…
## $ Count <dbl> 982, 32724, 13071, 260, 8916, 1830, 16595, 6138, 3579, 19090, 53…
## $ CPUE  <dbl> 5.3224932, 187.6711599, 12.7715195, 0.3697516, 11.8979680, 3.299…
# Print plot of annual CPUE
plt_cpue_yr

echo

If you want the code to be executed and results to be displayed in the output file when the Rmd is knitted, but for the code to NOT appear in the output file, set echo = FALSE as a code chunk option. For example, maybe you want glimpse(df_annual) and the plot of annual CPUE to display, but not show the code used to execute this.

## Rows: 67
## Columns: 4
## $ Year  <dbl> 1998, 1998, 1999, 1999, 2000, 2000, 2000, 2001, 2001, 2001, 2002…
## $ Gear  <chr> "BSEIN", "RSTR", "BSEIN", "FKTR", "BSEIN", "FKTR", "RSTR", "BSEI…
## $ Count <dbl> 982, 32724, 13071, 260, 8916, 1830, 16595, 6138, 3579, 19090, 53…
## $ CPUE  <dbl> 5.3224932, 187.6711599, 12.7715195, 0.3697516, 11.8979680, 3.299…

Figure output options

The default figure dimensions in a rendered R Markdown document is 7 inches wide and 5 inches tall. Sometimes you’ll want to change these, which can be done using the fig.height and fig.width code chunk options. Let’s change the dimensions of the annual CPUE figure to 6 by 6.

plt_cpue_yr

Now, maybe we want the figure to be aligned on the right-side of the knitted document. In this case, we’ll use the fig.align code chunk option.

plt_cpue_yr

If we want to add a caption to our figure, we’ll use the fig.cap code chunk option.

plt_cpue_yr
Figure 1: This is our annual CPUE figure.

Figure 1: This is our annual CPUE figure.

Finally, if we want to add alt text to our figure, we’ll use the fig.alt option.

plt_cpue_yr

This figure shows the annual CPUE for each gear type used by the YBFMP from 1998-2020.

Tables

This is what it looks like when you print a data frame itself.

df_annual
## # A tibble: 67 × 4
##     Year Gear  Count    CPUE
##    <dbl> <chr> <dbl>   <dbl>
##  1  1998 BSEIN   982   5.32 
##  2  1998 RSTR  32724 188.   
##  3  1999 BSEIN 13071  12.8  
##  4  1999 FKTR    260   0.370
##  5  2000 BSEIN  8916  11.9  
##  6  2000 FKTR   1830   3.30 
##  7  2000 RSTR  16595  30.5  
##  8  2001 BSEIN  6138  24.7  
##  9  2001 FKTR   3579   4.98 
## 10  2001 RSTR  19090  28.1  
## # … with 57 more rows

knitr::kable() offers a simple format for displaying tables in a rendered R Markdown document. For example, here is the table of annual count and CPUE data printed as a kable.

kable(df_annual, caption = "Annual Counts and CPUE of YBFMP survey data")
Annual Counts and CPUE of YBFMP survey data
Year Gear Count CPUE
1998 BSEIN 982 5.3224932
1998 RSTR 32724 187.6711599
1999 BSEIN 13071 12.7715195
1999 FKTR 260 0.3697516
2000 BSEIN 8916 11.8979680
2000 FKTR 1830 3.2997186
2000 RSTR 16595 30.4987486
2001 BSEIN 6138 24.7352013
2001 FKTR 3579 4.9793030
2001 RSTR 19090 28.0687376
2002 BSEIN 5386 26.4395986
2002 FKTR 2223 3.2344627
2002 RSTR 42362 63.1653017
2003 BSEIN 455 4.6880205
2003 FKTR 3311 4.9792709
2003 RSTR 13383 20.3652883
2004 BSEIN 1261 6.7650673
2004 FKTR 4369 6.5649613
2004 RSTR 19179 28.6243865
2005 BSEIN 611 4.1804482
2005 FKTR 2029 3.0151784
2005 RSTR 12181 20.4830891
2006 BSEIN 4988 14.9033394
2006 FKTR 1374 3.0068944
2006 RSTR 27915 65.6020561
2007 BSEIN 6205 6.7106441
2007 FKTR 5174 7.6289034
2007 RSTR 34692 55.4470753
2008 BSEIN 13144 11.9853387
2008 FKTR 2788 4.4703717
2008 RSTR 12329 46.9071342
2009 BSEIN 8144 19.9055054
2009 FKTR 3754 6.9650768
2009 RSTR 24944 58.3251513
2010 BSEIN 8889 6.8781580
2010 FKTR 1990 6.4066673
2010 RSTR 17814 73.4114551
2011 BSEIN 30131 11.0662723
2011 FKTR 1726 5.8182464
2011 RSTR 17761 59.5595626
2012 BSEIN 38990 16.7569638
2012 FKTR 2438 7.2846733
2012 RSTR 24999 90.8836115
2013 BSEIN 19977 10.3254081
2013 FKTR 1394 4.2841587
2013 RSTR 21361 60.8719818
2014 BSEIN 13941 8.2754013
2014 FKTR 1756 7.2168245
2014 RSTR 25016 71.7300402
2015 BSEIN 11075 6.3747209
2015 FKTR 2079 6.0665883
2015 RSTR 12612 33.1259513
2016 BSEIN 19600 10.5877517
2016 FKTR 2996 11.5283032
2016 RSTR 18675 64.6274886
2017 BSEIN 26777 13.6441580
2017 FKTR 4436 18.6973393
2017 RSTR 10283 68.1595985
2018 BSEIN 11688 7.3778899
2018 FKTR 6776 20.9096576
2018 RSTR 3212 9.5898324
2019 BSEIN 18541 14.2690841
2019 FKTR 5284 17.1477588
2019 RSTR 33746 119.1001649
2020 BSEIN 932 0.5414329
2020 FKTR 1851 6.4073520
2020 RSTR 2188 7.3031628

This is pretty nice compared to just printing the data frame itself. However, the kable formatting can be improved. The knitr::kable() function allows for some minor formatting such as rounding digits and column alignment.

kable(
  df_annual, 
  caption = "Annual Counts and CPUE of YBFMP survey data",
  # Round CPUE column to 1 digit
  digits = 1,
  # Align all columns to the left
  align = "l"
)
Annual Counts and CPUE of YBFMP survey data
Year Gear Count CPUE
1998 BSEIN 982 5.3
1998 RSTR 32724 187.7
1999 BSEIN 13071 12.8
1999 FKTR 260 0.4
2000 BSEIN 8916 11.9
2000 FKTR 1830 3.3
2000 RSTR 16595 30.5
2001 BSEIN 6138 24.7
2001 FKTR 3579 5.0
2001 RSTR 19090 28.1
2002 BSEIN 5386 26.4
2002 FKTR 2223 3.2
2002 RSTR 42362 63.2
2003 BSEIN 455 4.7
2003 FKTR 3311 5.0
2003 RSTR 13383 20.4
2004 BSEIN 1261 6.8
2004 FKTR 4369 6.6
2004 RSTR 19179 28.6
2005 BSEIN 611 4.2
2005 FKTR 2029 3.0
2005 RSTR 12181 20.5
2006 BSEIN 4988 14.9
2006 FKTR 1374 3.0
2006 RSTR 27915 65.6
2007 BSEIN 6205 6.7
2007 FKTR 5174 7.6
2007 RSTR 34692 55.4
2008 BSEIN 13144 12.0
2008 FKTR 2788 4.5
2008 RSTR 12329 46.9
2009 BSEIN 8144 19.9
2009 FKTR 3754 7.0
2009 RSTR 24944 58.3
2010 BSEIN 8889 6.9
2010 FKTR 1990 6.4
2010 RSTR 17814 73.4
2011 BSEIN 30131 11.1
2011 FKTR 1726 5.8
2011 RSTR 17761 59.6
2012 BSEIN 38990 16.8
2012 FKTR 2438 7.3
2012 RSTR 24999 90.9
2013 BSEIN 19977 10.3
2013 FKTR 1394 4.3
2013 RSTR 21361 60.9
2014 BSEIN 13941 8.3
2014 FKTR 1756 7.2
2014 RSTR 25016 71.7
2015 BSEIN 11075 6.4
2015 FKTR 2079 6.1
2015 RSTR 12612 33.1
2016 BSEIN 19600 10.6
2016 FKTR 2996 11.5
2016 RSTR 18675 64.6
2017 BSEIN 26777 13.6
2017 FKTR 4436 18.7
2017 RSTR 10283 68.2
2018 BSEIN 11688 7.4
2018 FKTR 6776 20.9
2018 RSTR 3212 9.6
2019 BSEIN 18541 14.3
2019 FKTR 5284 17.1
2019 RSTR 33746 119.1
2020 BSEIN 932 0.5
2020 FKTR 1851 6.4
2020 RSTR 2188 7.3

The kableExtra R package allows for many more formatting options for kables. As of version 1.2+, the author of kableExtra recommends using the kbl() function to create the kable.

library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
df_annual %>%
  kbl(
    caption = "Annual Counts and CPUE of YBFMP survey data",
    # Round CPUE column to 1 digit
    digits = 1,
    # Align all columns to the left
    align = "l"
  ) %>% 
  kable_styling(
    # appearance option
    bootstrap_options = c("striped", "hover"),
    # change width
    full_width = FALSE,
    # change position
    position = "left",
    # fix the header row at the top when scrolling, useful for longer tables
    fixed_thead = TRUE
  ) %>%
  # Format the header row (0) to be aligned in the center
  row_spec(0, align = "center") %>%
  # Make the Year column bold
  column_spec(1, bold = TRUE) %>%
  # add a scroll box for easier viewing
  scroll_box(width = "40%", height = "550px")
Annual Counts and CPUE of YBFMP survey data
Year Gear Count CPUE
1998 BSEIN 982 5.3
1998 RSTR 32724 187.7
1999 BSEIN 13071 12.8
1999 FKTR 260 0.4
2000 BSEIN 8916 11.9
2000 FKTR 1830 3.3
2000 RSTR 16595 30.5
2001 BSEIN 6138 24.7
2001 FKTR 3579 5.0
2001 RSTR 19090 28.1
2002 BSEIN 5386 26.4
2002 FKTR 2223 3.2
2002 RSTR 42362 63.2
2003 BSEIN 455 4.7
2003 FKTR 3311 5.0
2003 RSTR 13383 20.4
2004 BSEIN 1261 6.8
2004 FKTR 4369 6.6
2004 RSTR 19179 28.6
2005 BSEIN 611 4.2
2005 FKTR 2029 3.0
2005 RSTR 12181 20.5
2006 BSEIN 4988 14.9
2006 FKTR 1374 3.0
2006 RSTR 27915 65.6
2007 BSEIN 6205 6.7
2007 FKTR 5174 7.6
2007 RSTR 34692 55.4
2008 BSEIN 13144 12.0
2008 FKTR 2788 4.5
2008 RSTR 12329 46.9
2009 BSEIN 8144 19.9
2009 FKTR 3754 7.0
2009 RSTR 24944 58.3
2010 BSEIN 8889 6.9
2010 FKTR 1990 6.4
2010 RSTR 17814 73.4
2011 BSEIN 30131 11.1
2011 FKTR 1726 5.8
2011 RSTR 17761 59.6
2012 BSEIN 38990 16.8
2012 FKTR 2438 7.3
2012 RSTR 24999 90.9
2013 BSEIN 19977 10.3
2013 FKTR 1394 4.3
2013 RSTR 21361 60.9
2014 BSEIN 13941 8.3
2014 FKTR 1756 7.2
2014 RSTR 25016 71.7
2015 BSEIN 11075 6.4
2015 FKTR 2079 6.1
2015 RSTR 12612 33.1
2016 BSEIN 19600 10.6
2016 FKTR 2996 11.5
2016 RSTR 18675 64.6
2017 BSEIN 26777 13.6
2017 FKTR 4436 18.7
2017 RSTR 10283 68.2
2018 BSEIN 11688 7.4
2018 FKTR 6776 20.9
2018 RSTR 3212 9.6
2019 BSEIN 18541 14.3
2019 FKTR 5284 17.1
2019 RSTR 33746 119.1
2020 BSEIN 932 0.5
2020 FKTR 1851 6.4
2020 RSTR 2188 7.3


You can do so much more with the kableExtra R package. Check out the package documentation for more details and options.

Interactive Elements

There are a whole lot of R packages that allow for some really nice interactive widgets in R Markdown. We’ll cover three of these that I’ve used and found helpful.

datatable

You can convert a data frame to an interactive table using the DT::datatable() function.

library(DT)
datatable(df_annual)

There are also some options that you can set within the DT::datatable() function.

df_annual %>% 
  # Convert Year and Gear variables to factors to allow for select lists in the
    # datatable filters
  mutate(across(c(Year, Gear), factor)) %>% 
  datatable(
    # remove rownames
    rownames = FALSE,
    # add column filters
    filter = "top",
    options = list(autoWidth = TRUE)
  )

plotly

You can create interactive plots using the plotly R package. The plotly::ggplotly() function converts ggplot2 objects into interactive plots. I found that it works best with less complicated plots.

library(plotly)
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
ggplotly(plt_cpue_yr)

leaflet

You can make interactive maps using the leaflet R package. First we’ll download the fish monitoring stations for YBFMP from the EDI data package.

df_stations <- read_csv("https://portal.edirepository.org/nis/dataviewer?packageid=edi.233.3&entityid=89146f1382d7dfa3bbf3e4b1554eb5cc")
## Rows: 26 Columns: 9
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): StationCode, StationName, StationNumber, PeriodOfRecordFrom, Period...
## dbl (2): Latitude, Longitude
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Convert to sf object
library(sf)
## Linking to GEOS 3.9.1, GDAL 3.4.3, PROJ 7.2.1; sf_use_s2() is TRUE
sf_stations <- df_stations %>% st_as_sf(coords = c("Longitude", "Latitude"), crs = 4326)

Next, we’ll create an interactive map of the sampling locations.

library(leaflet)
sf_stations %>% 
  leaflet() %>% 
  addTiles() %>%
  addCircleMarkers(
    radius = 5,
    fillOpacity = 0.8,
    weight = 0.5,
    color = "black",
    opacity = 1
  )

Now, we’ll add labels for each station and color code the markers by the MethodCode (BSEIN, FKTR, RSTR).

# Define color palette for MethodCode
color_pal <- colorFactor(palette = "viridis", domain = sf_stations$MethodCode)

sf_stations %>% 
  leaflet() %>% 
  addTiles() %>%
  addCircleMarkers(
    radius = 5,
    fillColor = ~color_pal(MethodCode),
    fillOpacity = 0.8,
    weight = 0.5,
    color = "black",
    opacity = 1,
    label = paste0("Station Code: ", sf_stations$StationCode)
  ) %>% 
  addLegend(
    position = "topright",
    pal = color_pal,
    values = sf_stations$MethodCode,
    title = "Gear Type:"
  )

Adding Tabs

A feature that I’ve found to be useful is using tabs in a rendered R Markdown document. Here is how you would set up the document to display the two CPUE plots and the stations map in tabs for easier navigation. Apply the {.tabset} class attribute to the ## YBFMP Data Summary header below like this: ## YBFMP Data Summary {.tabset}. Then, each of the sub-headers of this header will appear as tabs instead of standalone sections. Use another header of the same level as ## YBFMP Data Summary (level 2) to stop using tab navigation.

YBFMP Data Summary

Monthly CPUE

Annual CPUE

Stations Map

Tab formatting

Add .tabset-fade and/or .tabset-pills to the {.tabset} class attribute to change the appearance and behavior of the tabs.

LS0tDQp0aXRsZTogIlIgTWFya2Rvd24gVHV0b3JpYWwgLSBBY3Rpdml0eSBFeGFtcGxlcyINCmF1dGhvcjogIkRhdmUgQm9zd29ydGgiDQpkYXRlOiAnYHIgZm9ybWF0KFN5cy5EYXRlKCksICIlQiAlZCwgJVkiKWAnDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyBQdXJwb3NlDQoNCkludHJvZHVjZSBhbmQgZGlzcGxheSBleGFtcGxlcyBvZiBob3cgdG8gdXNlIFIgTWFya2Rvd24gdG8gYnJpbmcgdG9nZXRoZXIgY29kZSBhbmQgdGV4dCBpbiBvbmUgZG9jdW1lbnQgdG8gc2hhcmUgeW91ciB3b3JrLg0KDQojIEdsb2JhbCBjb2RlIGFuZCBmdW5jdGlvbnMNCg0KYGBge3IgbG9hZCBwYWNrYWdlc30NCiMgTG9hZCBwYWNrYWdlcw0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShoZXJlKQ0KYGBgDQoNCiMgSW1wb3J0IERhdGENCg0KTGV0J3MgaW1wb3J0IHNvbWUgZXhhbXBsZSBmaXNoIGNvdW50IGRhdGEgY29sbGVjdGVkIGJ5IFlCRk1QLiANCg0KYGBge3IgaW1wb3J0IGRhdGF9DQpkZl9tb250aGx5IDwtIHJlYWRfY3N2KGhlcmUoIm1hdGVyaWFscy9Nb250aGx5X0NhdGNoX0NQVUUuY3N2IikpDQpgYGANCg0KVGhpcyBkYXRhIGNvbnRhaW5zIG1vbnRobHkgZmlzaCBjb3VudHMgYW5kIENQVUUgZm9yIHRocmVlIGRpZmZlcmVudCBnZWFyIHR5cGVzIChgciB1bmlxdWUoZGZfbW9udGhseSRHZWFyKWApIGZyb20gYHIgbWluKGRmX21vbnRobHkkeWVhcilgIHRvIGByIG1heChkZl9tb250aGx5JHllYXIpYC4NCg0KKioqDQoNCioqQSB3b3JkIGFib3V0IHdvcmtpbmcgZGlyZWN0b3JpZXMgYW5kIC5SbWQgZmlsZXM6KiogIA0KVGhlIGRlZmF1bHQgd29ya2luZyBkaXJlY3Rvcnkgd2hlbiBhbiAuUm1kIGZpbGUgaXMga25pdHRlZCBpcyB0aGUgLlJtZCBkb2N1bWVudCBkaXJlY3RvcnkuIFRoaXMgY2FuIGdldCBjb25mdXNpbmcgd2hlbiB5b3UgYXJlIHVzaW5nIGFuIFIgcHJvamVjdCwgaW4gd2hpY2ggdGhlIGRlZmF1bHQgd29ya2luZyBkaXJlY3RvcnkgaXMgdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoZSBSIHByb2plY3QuIFNvIGl0J3MgcG9zc2libGUgdG8gaGF2ZSBkaWZmZXJlbnQgd29ya2luZyBkaXJlY3RvcmllcyB3aGVuIHlvdSBhcmUgcnVubmluZyBjb2RlIGZyb20gY29kZSBibG9ja3MgaW4gYW4gLlJtZCBmaWxlIHZlcnN1cyB3aGVuIHlvdSBrbml0IHRoZSAuUm1kIGZpbGUuIEEgdXNlZnVsIFIgcGFja2FnZSB0byBoZWxwIHdpdGggd29ya2luZyBkaXJlY3RvcmllcyBhbmQgZmlsZSBwYXRocyBpcyB0aGUgW2BoZXJlYCBSIHBhY2thZ2VdKGh0dHBzOi8vaGVyZS5yLWxpYi5vcmcvKS4gWW91IGNhbiBzZWUgdGhhdCB3ZSdyZSB1c2luZyBpdCBpbiB0aGUgY29kZSBjaHVuayBhYm92ZSB0byBjbGVhcmx5IGRlZmluZSB0aGUgZmlsZSBwYXRoIGZvciB0aGUgZmlzaCBkYXRhLiAgVGhlIFtgaGVyZTo6aGVyZSgpYF0oaHR0cHM6Ly9oZXJlLnItbGliLm9yZy9yZWZlcmVuY2UvaGVyZS5odG1sKSBmdW5jdGlvbiBwb2ludHMgdG8gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIGFuIFIgcHJvamVjdCByZWdhcmRsZXNzIG9mIHdoYXQgdGhlIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkgaXMuIEZvciBleGFtcGxlOg0KDQpgYGB7ciBoZXJlIGZ1bmN0aW9uIGV4YW1wbGV9DQojIEN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkNCmdldHdkKCkNCg0KIyBXb3JraW5nIGRpcmVjdG9yeSBkZWZpbmVkIGJ5IGhlcmU6OmhlcmUoKQ0KaGVyZSgpDQpgYGANCg0KWW91IGNhbiBjb250aW51ZSB0aGUgZmlsZSBwYXRoIHdpdGhpbiB0aGUgYGhlcmU6OmhlcmUoKWAgZnVuY3Rpb24gYXMgYSBjaGFyYWN0ZXIgc3RyaW5nLCB3aGljaCBhcHBlbmRzIHRoaXMgc3RyaW5nIHRvIHRoZSB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhlIFIgcHJvamVjdC4gRm9yIGV4YW1wbGUsIGhlcmUgaXMgdGhlIGZpbGUgcGF0aCB0byB0aGUgZmlzaCBkYXRhIGltcG9ydGVkIGFib3ZlOg0KDQpgYGB7ciBoZXJlIGZ1bmN0aW9uIGV4YW1wbGUgMn0NCiMgRGVmaW5lIGZpbGUgcGF0aCB0byBmaXNoIGRhdGEgcmVsYXRpdmUgdG8gdGhlIHJvb3QgZGlyZWN0b3J5IHVzaW5nIGhlcmU6OmhlcmUoKQ0KaGVyZSgibWF0ZXJpYWxzL01vbnRobHlfQ2F0Y2hfQ1BVRS5jc3YiKQ0KYGBgDQoNCiMgVXNpbmcgQ29kZSBDaHVua3MNCg0KTGV0J3MgcHJhY3RpY2UgdXNpbmcgY29kZSBjaHVua3MgdG8gY3JlYXRlIGFuZCBwcmludCBhIHBsb3QgZGlzcGxheWluZyBtb250aGx5IENQVUUgYnkgZ2VhciB0eXBlLg0KDQpgYGB7ciBjcHVlIHBsb3R9DQpwbHRfY3B1ZV9tb250aCA8LSBkZl9tb250aGx5ICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gRGF0ZSwgeSA9IENQVUUpKSArDQogIGdlb21fY29sKCkgKw0KICBmYWNldF93cmFwKHZhcnMoR2VhciksIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKyANCiAgdGhlbWVfYncoKQ0KDQpwbHRfY3B1ZV9tb250aA0KYGBgDQoNCiMgQ29kZSBDaHVuayBPcHRpb25zDQoNCk5vdyB0aGF0IHdlJ3ZlIHNlZW4gaG93IHRvIHVzZSBjb2RlIGNodW5rcyBpbiBhbiAuUm1kIGRvY3VtZW50LCBsZXRzIHRha2UgYSBsb29rIGF0IHNvbWUgY29tbW9ubHktdXNlZCBjb2RlIGNodW5rIG9wdGlvbnMgdGhhdCBjb25maWd1cmUgaWYgYW5kIGhvdyB0aGUgY29kZSBjaHVuayBhbmQgaXRzIHJlc3VsdHMgYXJlIGRpc3BsYXllZC4NCg0KIyMgbWVzc2FnZSwgd2FybmluZw0KDQpOb3RpY2Ugd2hlbiB3ZSBpbXBvcnRlZCB0aGUgZmlzaCBjb3VudCBkYXRhIGFib3ZlLCB0aGVyZSBpcyBhIG1lc3NhZ2UgcHJpbnRlZCBpbiB0aGUgb3V0cHV0LiBJZiB3ZSBkb24ndCB3YW50IHRoaXMgbWVzc2FnZSB0byBkaXNwbGF5LCB3ZSBjYW4gc2V0IGBtZXNzYWdlID0gRkFMU0VgIGZvciB0aGUgY29kZSBjaHVuay4NCg0KYGBge3IgaW1wb3J0IGRhdGEgMiwgbWVzc2FnZSA9IEZBTFNFfQ0KZGZfbW9udGhseSA8LSByZWFkX2NzdihoZXJlKCJtYXRlcmlhbHMvTW9udGhseV9DYXRjaF9DUFVFLmNzdiIpKQ0KYGBgDQoNCk5vdyB0aGUgbWVzc2FnZSBkb2Vzbid0IGRpc3BsYXksIGJ1dCB0aGUgY29kZSB3YXMgc3RpbGwgZXhlY3V0ZWQuIFNhbWUgYXBwbGllcyB0byB3YXJuaW5ncy4NCg0KIyMgZXZhbA0KDQpJZiB5b3Ugd2FudCB0aGUgY29kZSB0byBhcHBlYXIgaW4gdGhlIG91dHB1dCBmaWxlLCBidXQgZm9yIHRoZSAqKmNvZGUgdG8gTk9UIHJ1bioqIHdoZW4gdGhlIFJtZCBmaWxlIGlzIGtuaXR0ZWQsIHNldCBgZXZhbCA9IEZBTFNFYCBhcyBhIGNvZGUgY2h1bmsgb3B0aW9uLiBGb3IgZXhhbXBsZSwgeW91IG1heSB3YW50IHRvIHNob3cgdGhlIGNvZGUgeW91IHVzZWQgdG8gc3VtbWFyaXplIHRoZSBjb3VudCBhbmQgQ1BVRSBkYXRhIGFzIGFubnVhbCB0b3RhbHMgaW4gYSBuZXcgZGF0YSBmcmFtZSBgZGZfYW5udWFsYCBhbmQgcGxvdCB0aGUgQ1BVRSByZXN1bHRzIHNhdmluZyBpdCBhcyBgcGx0X2NwdWVfeXJgLCBidXQgbm90IGFjdHVhbGx5IHJ1biB0aGUgY29kZSB3aGVuIHlvdSBrbml0IHRoZSBmaWxlLg0KDQpgYGB7ciBldmFsIGZhbHNlIGV4YW1wbGUsIGV2YWwgPSBGQUxTRX0NCiMgU3VtbWFyaXplIGNvdW50IGFuZCBDUFVFIGFzIGFubnVhbCB0b3RhbHMNCmRmX2FubnVhbCA8LSBkZl9tb250aGx5ICU+JSANCiAgZ3JvdXBfYnkoeWVhciwgR2VhcikgJT4lIA0KICBzdW1tYXJpemUoYWNyb3NzKGMobW8uY291bnQsIENQVUUpLCBzdW0pLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgcmVuYW1lKA0KICAgIFllYXIgPSB5ZWFyLA0KICAgIENvdW50ID0gbW8uY291bnQNCiAgKQ0KDQojIFBsb3QgYW5udWFsIENQVUUNCnBsdF9jcHVlX3lyIDwtIGRmX2FubnVhbCAlPiUgDQogIGdncGxvdChhZXMoeCA9IFllYXIsIHkgPSBDUFVFKSkgKw0KICBnZW9tX2NvbCgpICsNCiAgZmFjZXRfd3JhcCh2YXJzKEdlYXIpLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeSIpICsgDQogIHRoZW1lX2J3KCkNCg0KcGx0X2NwdWVfeXINCmBgYA0KDQpOb3cgaWYgeW91IHdlcmUgdG8gdHJ5IHRvIHByaW50IGBkZl9hbm51YWxgIGFuZCBgcGx0X2NwdWVfeXJgLCB5b3UnbGwgZ2V0IGFuIGVycm9yIHNpbmNlIHRoZSBjb2RlIHdhc24ndCBleGVjdXRlZCB3aGVuIHRoZSBkb2N1bWVudCB3YXMga25pdHRlZC4NCg0KYGBge3IgZXJyb3IgZGZfYW5udWFsIGFuZCBwbHRfY3B1ZV95ciwgZXJyb3IgPSBUUlVFfQ0KIyBTaG93IGdsaW1wc2Ugb2YgZGZfYW5udWFsDQpnbGltcHNlKGRmX2FubnVhbCkNCg0KIyBQcmludCBwbG90IG9mIGFubnVhbCBDUFVFDQpwbHRfY3B1ZV95cg0KYGBgDQoNCiMjIGluY2x1ZGUNCg0KSWYgeW91IHdhbnQgdGhlIGNvZGUgdG8gYmUgZXhlY3V0ZWQgd2hlbiB0aGUgUm1kIGZpbGUgaXMga25pdHRlZCwgYnV0IGZvciB0aGUgKipjb2RlIGFuZCByZXN1bHRzIHRvIE5PVCBhcHBlYXIqKiBpbiB0aGUgb3V0cHV0IGZpbGUsIHNldCBgaW5jbHVkZSA9IEZBTFNFYCBhcyBhIGNvZGUgY2h1bmsgb3B0aW9uLiBGb3IgZXhhbXBsZSwgbGV0J3Mgc3VtbWFyaXplIGFuZCBwbG90IHRoZSBDUFVFIGRhdGEgdXNpbmcgdGhlIHNhbWUgY29kZSBzaG93biBhYm92ZSwgYnV0IHRoaXMgdGltZSB3ZSdsbCB1c2UgYGluY2x1ZGUgPSBGQUxTRWAgc28gdGhlIGNvZGUgYW5kIHJlc3VsdHMgd29uJ3QgYXBwZWFyIGluIHRoZSBvdXRwdXQgZG9jdW1lbnQuDQoNCmBgYHtyIGluY2x1ZGUgZmFsc2UgZXhhbXBsZSwgaW5jbHVkZSA9IEZBTFNFfQ0KIyBTdW1tYXJpemUgY291bnQgYW5kIENQVUUgYXMgYW5udWFsIHRvdGFscw0KZGZfYW5udWFsIDwtIGRmX21vbnRobHkgJT4lIA0KICBncm91cF9ieSh5ZWFyLCBHZWFyKSAlPiUgDQogIHN1bW1hcml6ZShhY3Jvc3MoYyhtby5jb3VudCwgQ1BVRSksIHN1bSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICByZW5hbWUoDQogICAgWWVhciA9IHllYXIsDQogICAgQ291bnQgPSBtby5jb3VudA0KICApDQoNCiMgUGxvdCBhbm51YWwgQ1BVRQ0KcGx0X2NwdWVfeXIgPC0gZGZfYW5udWFsICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IENQVUUpKSArDQogIGdlb21fY29sKCkgKw0KICBmYWNldF93cmFwKHZhcnMoR2VhciksIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKyANCiAgdGhlbWVfYncoKQ0KDQpwbHRfY3B1ZV95cg0KYGBgDQoNCkV2ZW4gdGhvdWdoIHRoZSBjb2RlIGFuZCByZXN1bHRzIGFyZW4ndCBkaXNwbGF5ZWQgaW4gdGhlIGRvY3VtZW50LCB0aGUgYGRmX2FubnVhbGAgZGF0YSBmcmFtZSBhbmQgYHBsdF9jcHVlX3lyYCBwbG90IG9iamVjdCBhcmUgYXZhaWxhYmxlIHRvIGJlIHVzZWQgd2l0aGluIHRoZSBSbWQgZmlsZS4NCg0KYGBge3Igc2hvdyBkZl9hbm51YWwgYW5kIHBsdF9jcHVlX3lyfQ0KIyBTaG93IGdsaW1wc2Ugb2YgZGZfYW5udWFsDQpnbGltcHNlKGRmX2FubnVhbCkNCg0KIyBQcmludCBwbG90IG9mIGFubnVhbCBDUFVFDQpwbHRfY3B1ZV95cg0KYGBgDQoNCiMjIGVjaG8NCg0KSWYgeW91IHdhbnQgdGhlIGNvZGUgdG8gYmUgZXhlY3V0ZWQgYW5kIHJlc3VsdHMgdG8gYmUgZGlzcGxheWVkIGluIHRoZSBvdXRwdXQgZmlsZSB3aGVuIHRoZSBSbWQgaXMga25pdHRlZCwgYnV0IGZvciB0aGUgKipjb2RlIHRvIE5PVCBhcHBlYXIqKiBpbiB0aGUgb3V0cHV0IGZpbGUsIHNldCBgZWNobyA9IEZBTFNFYCBhcyBhIGNvZGUgY2h1bmsgb3B0aW9uLiBGb3IgZXhhbXBsZSwgbWF5YmUgeW91IHdhbnQgYGdsaW1wc2UoZGZfYW5udWFsKWAgYW5kIHRoZSBwbG90IG9mIGFubnVhbCBDUFVFIHRvIGRpc3BsYXksIGJ1dCBub3Qgc2hvdyB0aGUgY29kZSB1c2VkIHRvIGV4ZWN1dGUgdGhpcy4NCg0KYGBge3IgZWNobyBmYWxzZSBleGFtcGxlLCBlY2hvID0gRkFMU0V9DQojIFNob3cgZ2xpbXBzZSBvZiBkZl9hbm51YWwNCmdsaW1wc2UoZGZfYW5udWFsKQ0KDQojIFByaW50IHBsb3Qgb2YgYW5udWFsIENQVUUNCnBsdF9jcHVlX3lyDQpgYGANCg0KIyMgRmlndXJlIG91dHB1dCBvcHRpb25zDQoNClRoZSBkZWZhdWx0IGZpZ3VyZSBkaW1lbnNpb25zIGluIGEgcmVuZGVyZWQgUiBNYXJrZG93biBkb2N1bWVudCBpcyA3IGluY2hlcyB3aWRlIGFuZCA1IGluY2hlcyB0YWxsLiBTb21ldGltZXMgeW91J2xsIHdhbnQgdG8gY2hhbmdlIHRoZXNlLCB3aGljaCBjYW4gYmUgZG9uZSB1c2luZyB0aGUgYGZpZy5oZWlnaHRgIGFuZCBgZmlnLndpZHRoYCBjb2RlIGNodW5rIG9wdGlvbnMuIExldCdzIGNoYW5nZSB0aGUgZGltZW5zaW9ucyBvZiB0aGUgYW5udWFsIENQVUUgZmlndXJlIHRvIDYgYnkgNi4NCg0KYGBge3IgcGxvdCBhbm51YWwgY3B1ZSBjaGFuZ2UgZGltcywgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDZ9DQpwbHRfY3B1ZV95cg0KYGBgDQoNCk5vdywgbWF5YmUgd2Ugd2FudCB0aGUgZmlndXJlIHRvIGJlIGFsaWduZWQgb24gdGhlIHJpZ2h0LXNpZGUgb2YgdGhlIGtuaXR0ZWQgZG9jdW1lbnQuIEluIHRoaXMgY2FzZSwgd2UnbGwgdXNlIHRoZSBgZmlnLmFsaWduYCBjb2RlIGNodW5rIG9wdGlvbi4NCg0KYGBge3IgcGxvdCBhbm51YWwgY3B1ZSBjaGFuZ2UgYWxpZ24sIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA2LCBmaWcuYWxpZ24gPSAncmlnaHQnfQ0KcGx0X2NwdWVfeXINCmBgYA0KDQpJZiB3ZSB3YW50IHRvIGFkZCBhIGNhcHRpb24gdG8gb3VyIGZpZ3VyZSwgd2UnbGwgdXNlIHRoZSBgZmlnLmNhcGAgY29kZSBjaHVuayBvcHRpb24uDQoNCmBgYHtyIHBsb3QgYW5udWFsIGNwdWUgYWRkIGNhcHRpb24sIGZpZy5jYXAgPSAiRmlndXJlIDE6IFRoaXMgaXMgb3VyIGFubnVhbCBDUFVFIGZpZ3VyZS4ifQ0KcGx0X2NwdWVfeXINCmBgYA0KDQpGaW5hbGx5LCBpZiB3ZSB3YW50IHRvIGFkZCBhbHQgdGV4dCB0byBvdXIgZmlndXJlLCB3ZSdsbCB1c2UgdGhlIGBmaWcuYWx0YCBvcHRpb24uDQoNCmBgYHtyIGFubnVhbCBjcHVlIGFsdCB0ZXh0LCBpbmNsdWRlID0gRkFMU0V9DQpwbHRfY3B1ZV95cl9hbHRleHQgPC0gIlRoaXMgZmlndXJlIHNob3dzIHRoZSBhbm51YWwgQ1BVRSBmb3IgZWFjaCBnZWFyIHR5cGUgdXNlZCBieSB0aGUgWUJGTVAgZnJvbSAxOTk4LTIwMjAuIg0KYGBgDQoNCmBgYHtyIHBsb3QgYW5udWFsIGNwdWUgYWRkIGFsdCB0ZXh0LCBmaWcuYWx0PSBwbHRfY3B1ZV95cl9hbHRleHR9DQpwbHRfY3B1ZV95cg0KYGBgDQoNCiMgVGFibGVzDQoNClRoaXMgaXMgd2hhdCBpdCBsb29rcyBsaWtlIHdoZW4geW91IHByaW50IGEgZGF0YSBmcmFtZSBpdHNlbGYuDQoNCmBgYHtyIGFubnVhbCBkYXRhIGFzIHRpYmJsZX0NCmRmX2FubnVhbA0KYGBgDQoNCmBrbml0cjo6a2FibGUoKWAgb2ZmZXJzIGEgc2ltcGxlIGZvcm1hdCBmb3IgZGlzcGxheWluZyB0YWJsZXMgaW4gYSByZW5kZXJlZCBSIE1hcmtkb3duIGRvY3VtZW50LiBGb3IgZXhhbXBsZSwgaGVyZSBpcyB0aGUgdGFibGUgb2YgYW5udWFsIGNvdW50IGFuZCBDUFVFIGRhdGEgcHJpbnRlZCBhcyBhIGthYmxlLg0KDQpgYGB7ciBhbm51YWwgZGF0YSBhcyBrYWJsZX0NCmthYmxlKGRmX2FubnVhbCwgY2FwdGlvbiA9ICJBbm51YWwgQ291bnRzIGFuZCBDUFVFIG9mIFlCRk1QIHN1cnZleSBkYXRhIikNCmBgYA0KDQpUaGlzIGlzIHByZXR0eSBuaWNlIGNvbXBhcmVkIHRvIGp1c3QgcHJpbnRpbmcgdGhlIGRhdGEgZnJhbWUgaXRzZWxmLiBIb3dldmVyLCB0aGUga2FibGUgZm9ybWF0dGluZyBjYW4gYmUgaW1wcm92ZWQuIFRoZSBga25pdHI6OmthYmxlKClgIGZ1bmN0aW9uIGFsbG93cyBmb3Igc29tZSBtaW5vciBmb3JtYXR0aW5nIHN1Y2ggYXMgcm91bmRpbmcgZGlnaXRzIGFuZCBjb2x1bW4gYWxpZ25tZW50Lg0KDQpgYGB7ciBhbm51YWwgZGF0YSBhcyBrYWJsZSB3aXRoIGZvcm1hdH0NCmthYmxlKA0KICBkZl9hbm51YWwsIA0KICBjYXB0aW9uID0gIkFubnVhbCBDb3VudHMgYW5kIENQVUUgb2YgWUJGTVAgc3VydmV5IGRhdGEiLA0KICAjIFJvdW5kIENQVUUgY29sdW1uIHRvIDEgZGlnaXQNCiAgZGlnaXRzID0gMSwNCiAgIyBBbGlnbiBhbGwgY29sdW1ucyB0byB0aGUgbGVmdA0KICBhbGlnbiA9ICJsIg0KKQ0KYGBgDQoNClRoZSBga2FibGVFeHRyYWAgUiBwYWNrYWdlIGFsbG93cyBmb3IgbWFueSBtb3JlIGZvcm1hdHRpbmcgb3B0aW9ucyBmb3Iga2FibGVzLiBBcyBvZiB2ZXJzaW9uIDEuMissIHRoZSBhdXRob3Igb2YgYGthYmxlRXh0cmFgIHJlY29tbWVuZHMgdXNpbmcgdGhlIGBrYmwoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSBrYWJsZS4NCg0KYGBge3IgZm9ybWF0IGthYmxlfQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KDQpkZl9hbm51YWwgJT4lDQogIGtibCgNCiAgICBjYXB0aW9uID0gIkFubnVhbCBDb3VudHMgYW5kIENQVUUgb2YgWUJGTVAgc3VydmV5IGRhdGEiLA0KICAgICMgUm91bmQgQ1BVRSBjb2x1bW4gdG8gMSBkaWdpdA0KICAgIGRpZ2l0cyA9IDEsDQogICAgIyBBbGlnbiBhbGwgY29sdW1ucyB0byB0aGUgbGVmdA0KICAgIGFsaWduID0gImwiDQogICkgJT4lIA0KICBrYWJsZV9zdHlsaW5nKA0KICAgICMgYXBwZWFyYW5jZSBvcHRpb24NCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwNCiAgICAjIGNoYW5nZSB3aWR0aA0KICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwNCiAgICAjIGNoYW5nZSBwb3NpdGlvbg0KICAgIHBvc2l0aW9uID0gImxlZnQiLA0KICAgICMgZml4IHRoZSBoZWFkZXIgcm93IGF0IHRoZSB0b3Agd2hlbiBzY3JvbGxpbmcsIHVzZWZ1bCBmb3IgbG9uZ2VyIHRhYmxlcw0KICAgIGZpeGVkX3RoZWFkID0gVFJVRQ0KICApICU+JQ0KICAjIEZvcm1hdCB0aGUgaGVhZGVyIHJvdyAoMCkgdG8gYmUgYWxpZ25lZCBpbiB0aGUgY2VudGVyDQogIHJvd19zcGVjKDAsIGFsaWduID0gImNlbnRlciIpICU+JQ0KICAjIE1ha2UgdGhlIFllYXIgY29sdW1uIGJvbGQNCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JQ0KICAjIGFkZCBhIHNjcm9sbCBib3ggZm9yIGVhc2llciB2aWV3aW5nDQogIHNjcm9sbF9ib3god2lkdGggPSAiNDAlIiwgaGVpZ2h0ID0gIjU1MHB4IikNCmBgYA0KPGJyPg0KDQpZb3UgY2FuIGRvIHNvIG11Y2ggbW9yZSB3aXRoIHRoZSBga2FibGVFeHRyYWAgUiBwYWNrYWdlLiBDaGVjayBvdXQgdGhlIHBhY2thZ2UgIFtkb2N1bWVudGF0aW9uXShodHRwczovL2hhb3podTIzMy5naXRodWIuaW8va2FibGVFeHRyYS8pIGZvciBtb3JlIGRldGFpbHMgYW5kIG9wdGlvbnMuDQoNCiMgSW50ZXJhY3RpdmUgRWxlbWVudHMNCg0KVGhlcmUgYXJlIGEgd2hvbGUgbG90IG9mIFIgcGFja2FnZXMgdGhhdCBhbGxvdyBmb3Igc29tZSByZWFsbHkgbmljZSBpbnRlcmFjdGl2ZSB3aWRnZXRzIGluIFIgTWFya2Rvd24uIFdlJ2xsIGNvdmVyIHRocmVlIG9mIHRoZXNlIHRoYXQgSSd2ZSB1c2VkIGFuZCBmb3VuZCBoZWxwZnVsLg0KDQojIyBkYXRhdGFibGUNCg0KWW91IGNhbiBjb252ZXJ0IGEgZGF0YSBmcmFtZSB0byBhbiBpbnRlcmFjdGl2ZSB0YWJsZSB1c2luZyB0aGUgYERUOjpkYXRhdGFibGUoKWAgZnVuY3Rpb24uDQoNCmBgYHtyIHVzaW5nIGRhdGF0YWJsZX0NCmxpYnJhcnkoRFQpDQpkYXRhdGFibGUoZGZfYW5udWFsKQ0KYGBgDQoNClRoZXJlIGFyZSBhbHNvIHNvbWUgb3B0aW9ucyB0aGF0IHlvdSBjYW4gc2V0IHdpdGhpbiB0aGUgYERUOjpkYXRhdGFibGUoKWAgZnVuY3Rpb24uDQoNCmBgYHtyIGRhdGF0YWJsZSB3aXRoIG9wdGlvbnN9DQpkZl9hbm51YWwgJT4lIA0KICAjIENvbnZlcnQgWWVhciBhbmQgR2VhciB2YXJpYWJsZXMgdG8gZmFjdG9ycyB0byBhbGxvdyBmb3Igc2VsZWN0IGxpc3RzIGluIHRoZQ0KICAgICMgZGF0YXRhYmxlIGZpbHRlcnMNCiAgbXV0YXRlKGFjcm9zcyhjKFllYXIsIEdlYXIpLCBmYWN0b3IpKSAlPiUgDQogIGRhdGF0YWJsZSgNCiAgICAjIHJlbW92ZSByb3duYW1lcw0KICAgIHJvd25hbWVzID0gRkFMU0UsDQogICAgIyBhZGQgY29sdW1uIGZpbHRlcnMNCiAgICBmaWx0ZXIgPSAidG9wIiwNCiAgICBvcHRpb25zID0gbGlzdChhdXRvV2lkdGggPSBUUlVFKQ0KICApDQpgYGANCg0KIyMgcGxvdGx5DQoNCllvdSBjYW4gY3JlYXRlIGludGVyYWN0aXZlIHBsb3RzIHVzaW5nIHRoZSBgcGxvdGx5YCBSIHBhY2thZ2UuIFRoZSBgcGxvdGx5OjpnZ3Bsb3RseSgpYCBmdW5jdGlvbiBjb252ZXJ0cyBgZ2dwbG90MmAgb2JqZWN0cyBpbnRvIGludGVyYWN0aXZlIHBsb3RzLiBJIGZvdW5kIHRoYXQgaXQgd29ya3MgYmVzdCB3aXRoIGxlc3MgY29tcGxpY2F0ZWQgcGxvdHMuDQoNCmBgYHtyIHVzaW5nIHBsb3RseSwgd2FybmluZyA9IEZBTFNFfQ0KbGlicmFyeShwbG90bHkpDQpnZ3Bsb3RseShwbHRfY3B1ZV95cikNCmBgYA0KDQojIyBsZWFmbGV0DQoNCllvdSBjYW4gbWFrZSBpbnRlcmFjdGl2ZSBtYXBzIHVzaW5nIHRoZSBgbGVhZmxldGAgUiBwYWNrYWdlLiBGaXJzdCB3ZSdsbCBkb3dubG9hZCB0aGUgZmlzaCBtb25pdG9yaW5nIHN0YXRpb25zIGZvciBZQkZNUCBmcm9tIHRoZSBbRURJIGRhdGEgcGFja2FnZV0oaHR0cHM6Ly9wb3J0YWwuZWRpcmVwb3NpdG9yeS5vcmcvbmlzL21hcGJyb3dzZT9wYWNrYWdlaWQ9ZWRpLjIzMy4zKS4NCg0KYGBge3IgZG93bmxvYWQgYW5kIHByZXBhcmUgZmlzaCBzdGF0aW9uIGRhdGF9DQpkZl9zdGF0aW9ucyA8LSByZWFkX2NzdigiaHR0cHM6Ly9wb3J0YWwuZWRpcmVwb3NpdG9yeS5vcmcvbmlzL2RhdGF2aWV3ZXI/cGFja2FnZWlkPWVkaS4yMzMuMyZlbnRpdHlpZD04OTE0NmYxMzgyZDdkZmEzYmJmM2U0YjE1NTRlYjVjYyIpDQoNCiMgQ29udmVydCB0byBzZiBvYmplY3QNCmxpYnJhcnkoc2YpDQpzZl9zdGF0aW9ucyA8LSBkZl9zdGF0aW9ucyAlPiUgc3RfYXNfc2YoY29vcmRzID0gYygiTG9uZ2l0dWRlIiwgIkxhdGl0dWRlIiksIGNycyA9IDQzMjYpDQpgYGANCg0KTmV4dCwgd2UnbGwgY3JlYXRlIGFuIGludGVyYWN0aXZlIG1hcCBvZiB0aGUgc2FtcGxpbmcgbG9jYXRpb25zLg0KDQpgYGB7ciB1c2luZyBsZWFmbGV0fQ0KbGlicmFyeShsZWFmbGV0KQ0Kc2Zfc3RhdGlvbnMgJT4lIA0KICBsZWFmbGV0KCkgJT4lIA0KICBhZGRUaWxlcygpICU+JQ0KICBhZGRDaXJjbGVNYXJrZXJzKA0KICAgIHJhZGl1cyA9IDUsDQogICAgZmlsbE9wYWNpdHkgPSAwLjgsDQogICAgd2VpZ2h0ID0gMC41LA0KICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICBvcGFjaXR5ID0gMQ0KICApDQpgYGANCg0KTm93LCB3ZSdsbCBhZGQgbGFiZWxzIGZvciBlYWNoIHN0YXRpb24gYW5kIGNvbG9yIGNvZGUgdGhlIG1hcmtlcnMgYnkgdGhlIGBNZXRob2RDb2RlYCAoYHIgdW5pcXVlKHNmX3N0YXRpb25zJE1ldGhvZENvZGUpYCkuDQoNCmBgYHtyIGxlYWZsZXQgd2l0aCBtb3JlIG9wdGlvbnN9DQojIERlZmluZSBjb2xvciBwYWxldHRlIGZvciBNZXRob2RDb2RlDQpjb2xvcl9wYWwgPC0gY29sb3JGYWN0b3IocGFsZXR0ZSA9ICJ2aXJpZGlzIiwgZG9tYWluID0gc2Zfc3RhdGlvbnMkTWV0aG9kQ29kZSkNCg0Kc2Zfc3RhdGlvbnMgJT4lIA0KICBsZWFmbGV0KCkgJT4lIA0KICBhZGRUaWxlcygpICU+JQ0KICBhZGRDaXJjbGVNYXJrZXJzKA0KICAgIHJhZGl1cyA9IDUsDQogICAgZmlsbENvbG9yID0gfmNvbG9yX3BhbChNZXRob2RDb2RlKSwNCiAgICBmaWxsT3BhY2l0eSA9IDAuOCwNCiAgICB3ZWlnaHQgPSAwLjUsDQogICAgY29sb3IgPSAiYmxhY2siLA0KICAgIG9wYWNpdHkgPSAxLA0KICAgIGxhYmVsID0gcGFzdGUwKCJTdGF0aW9uIENvZGU6ICIsIHNmX3N0YXRpb25zJFN0YXRpb25Db2RlKQ0KICApICU+JSANCiAgYWRkTGVnZW5kKA0KICAgIHBvc2l0aW9uID0gInRvcHJpZ2h0IiwNCiAgICBwYWwgPSBjb2xvcl9wYWwsDQogICAgdmFsdWVzID0gc2Zfc3RhdGlvbnMkTWV0aG9kQ29kZSwNCiAgICB0aXRsZSA9ICJHZWFyIFR5cGU6Ig0KICApDQpgYGANCg0KIyBBZGRpbmcgVGFicw0KDQpBIGZlYXR1cmUgdGhhdCBJJ3ZlIGZvdW5kIHRvIGJlIHVzZWZ1bCBpcyB1c2luZyB0YWJzIGluIGEgcmVuZGVyZWQgUiBNYXJrZG93biBkb2N1bWVudC4gSGVyZSBpcyBob3cgeW91IHdvdWxkIHNldCB1cCB0aGUgZG9jdW1lbnQgdG8gZGlzcGxheSB0aGUgdHdvIENQVUUgcGxvdHMgYW5kIHRoZSBzdGF0aW9ucyBtYXAgaW4gdGFicyBmb3IgZWFzaWVyIG5hdmlnYXRpb24uIEFwcGx5IHRoZSBgey50YWJzZXR9YCBjbGFzcyBhdHRyaWJ1dGUgdG8gdGhlIGAjIyBZQkZNUCBEYXRhIFN1bW1hcnlgIGhlYWRlciBiZWxvdyBsaWtlIHRoaXM6IGAjIyBZQkZNUCBEYXRhIFN1bW1hcnkgey50YWJzZXR9YC4gVGhlbiwgZWFjaCBvZiB0aGUgc3ViLWhlYWRlcnMgb2YgdGhpcyBoZWFkZXIgd2lsbCBhcHBlYXIgYXMgdGFicyBpbnN0ZWFkIG9mIHN0YW5kYWxvbmUgc2VjdGlvbnMuIFVzZSBhbm90aGVyIGhlYWRlciBvZiB0aGUgc2FtZSBsZXZlbCBhcyBgIyMgWUJGTVAgRGF0YSBTdW1tYXJ5YCAobGV2ZWwgMikgdG8gc3RvcCB1c2luZyB0YWIgbmF2aWdhdGlvbi4NCg0KIyMgWUJGTVAgRGF0YSBTdW1tYXJ5IHsudGFic2V0fQ0KDQojIyMgTW9udGhseSBDUFVFDQoNCmBgYHtyIG1vbnRobHkgY3B1ZSBwbG90IHRhYiwgZWNobyA9IEZBTFNFfQ0KcGx0X2NwdWVfbW9udGgNCmBgYA0KDQojIyMgQW5udWFsIENQVUUNCg0KYGBge3IgYW5udWFsIGNwdWUgcGxvdCB0YWIsIGVjaG8gPSBGQUxTRX0NCnBsdF9jcHVlX3lyDQpgYGANCg0KIyMjIFN0YXRpb25zIE1hcA0KDQpgYGB7ciBzdGF0aW9ucyBtYXAgdGFiLCBlY2hvID0gRkFMU0V9DQpzZl9zdGF0aW9ucyAlPiUgDQogIGxlYWZsZXQoKSAlPiUgDQogIGFkZFRpbGVzKCkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgcmFkaXVzID0gNSwNCiAgICBmaWxsQ29sb3IgPSB+Y29sb3JfcGFsKE1ldGhvZENvZGUpLA0KICAgIGZpbGxPcGFjaXR5ID0gMC44LA0KICAgIHdlaWdodCA9IDAuNSwNCiAgICBjb2xvciA9ICJibGFjayIsDQogICAgb3BhY2l0eSA9IDEsDQogICAgbGFiZWwgPSBwYXN0ZTAoIlN0YXRpb24gQ29kZTogIiwgc2Zfc3RhdGlvbnMkU3RhdGlvbkNvZGUpDQogICkgJT4lIA0KICBhZGRMZWdlbmQoDQogICAgcG9zaXRpb24gPSAidG9wcmlnaHQiLA0KICAgIHBhbCA9IGNvbG9yX3BhbCwNCiAgICB2YWx1ZXMgPSBzZl9zdGF0aW9ucyRNZXRob2RDb2RlLA0KICAgIHRpdGxlID0gIkdlYXIgVHlwZToiDQogICkNCmBgYA0KDQojIyBUYWIgZm9ybWF0dGluZw0KDQpBZGQgYC50YWJzZXQtZmFkZWAgYW5kL29yIGAudGFic2V0LXBpbGxzYCB0byB0aGUgYHsudGFic2V0fWAgY2xhc3MgYXR0cmlidXRlIHRvIGNoYW5nZSB0aGUgYXBwZWFyYW5jZSBhbmQgYmVoYXZpb3Igb2YgdGhlIHRhYnMuDQoNCg==