Introduction
Programming is not easy and developing code is never a linear path without problems, rather it is a highway littered with frustration, anger, and eventual joy. Aside from proper doing analysis, planning carefully, leveraging and applying program and design patterns, and using sound programming practices, mistakes still occur and “bugs” must be located. Debugging is thus essential for any programming effort, including programming in R.
This lesson introduces common strategies for helping locate errors in R programs. Most of the practices shown apply only to actual “programs”, i.e., R scripts, and may not work well in R Markdown, R Notebooks, or shiny web apps. However, code can be copied from an R Notebook to an R Script for debugging purposes. The reason why R Markdown is not easily debugged is that the code is run during knitting where output and code is intercepted by the knitr package code.
Common Strategies
Debugging
This section presents a series of common strategies for dealing with bugs and errors.
Google
Whenever you encounter an error message, start by searching for the message online. Chances are that someone else has encountered this error before and there’s a solution posted. When “googling”, improve the likelihood of a good match by removing any variable names or values that are specific to your code and enclose your your error message in quotes when you use it as a search term.
Of course, be sure to acknowledge the solution in your code, post your own solutions, and contribute to knowledge basis – at least by “liking” an answer if it was helpful or explaining why an answer does not work or did not work for you.
Isolate Defective Code
Finding an error in a large program is nearly impossible. Start by extracting (into a separate source file) the code that does not work and reduce any data structure or files to a small sample that allows you to reproduce the error.
Debug in Sandbox
Do your debugging in separate code files. Do not debug in your production code if at all possible. Experiment in that sandbox.
Add Debug Statements
One of the most common debugging tricks is to insert various forms of print statements into the code to inform the programmer where the program is executing and what values of variables are. R provides numerous mechanisms for inserting debug statements into R code.
For more information on this approach, see the section on Debugging R Scripts below.
Use Interactive Debugger
An interactive debugger is often very helpful in tracking down errors. It allows a programmer to set breakpoints, inspect the call stack, and see the contents of variables.
For more information on how to use the interactive debugger in R Studio, see Lesson 6.195 Using the Debugger in R Studio.
Debugging R Scripts
There are a several common approaches for debugging your own code. Of course, carefully reading the error message and line number that R provides is often a first clue. Naturally, the error could be on the line prior so be sure to look there first.
In R Studio, use the Environment window tab to watch the value of variables as code executes.
Display diagnostic information, variable contents, run counters a combination of calls to print()
, cat()
or message()
.
Call traceback()
to get a stack trace to see where a reported error is occurring.
Call browser()
to open an interactive debugger window
Call debug()
to automatically open a debugger at the start of a function call
Call trace()
to set a breakpoint by opening a debugger at a location inside a function.
Diagnostic Messages
Below is a short piece of code that reads a CSV of cereals, cereal content, and name of manufacturer and then counts how many cereals each manufacturer produces. We will modify the code to demonstrate various debugging practices.
df <- read.csv(file = "CerealDataCSV.csv",
header = T,
stringsAsFactors = F)
# total number of cereals
n <- nrow(df)
# new data frame holding manufacturers and count
df.manu <- data.frame(Manufacturer = vector(mode = "character", 0),
NumCereals = vector(mode = "numeric", 0))
for (i in 1:n)
{
# next manufacturer in the data
m <- df$Manufacturer[i]
# is it already in the list of manufacturers?
f <- which(df.manu$Manufacturer == m)
if (length(f) == 0) {
# not yet -- so add manufacturer with count of 1
newRow <- nrow(df.manu)+1
df.manu[newRow,2] <- 1
df.manu[newRow,1] <- m
} else {
# yes -- bump the count
df.manu[f,2] <- df.manu[f,2] +1
}
}
print(head(df.manu, 3))
## Manufacturer NumCereals
## 1 Nabisco 6
## 2 Kellogg 21
## 3 Ralston Purina 6
If you code does not behave as expected, a common tactic is to add print()
or cat()
messages in your code and print out diagnostic messages that include values of variables and objects. Use str()
to print more details about an object’s structure.
One main drawback of this approach is that you cannot add print()
to functions or loops. In addition, if added to a long loop, the messages can be overwhelming. Finally, diagnostic messages take a relatively long time to render so code will run slower, particularly long loops. In that case, reduce the number of iterations of a loop or use a subset of a dataframe or file.
df <- read.csv(file = "CerealDataCSV.csv",
header = T,
stringsAsFactors = F)
# total number of cereals
n <- nrow(df)
# new data frame holding manufacturers and count
df.manu <- data.frame(Manufacturer = vector(mode = "character", 0),
NumCereals = vector(mode = "numeric", 0))
cat("Size of df.manu: ", str(df.manu), "\n")
## 'data.frame': 0 obs. of 2 variables:
## $ Manufacturer: chr
## $ NumCereals : num
## Size of df.manu:
for (i in 1:3)
{
# next manufacturer in the data
m <- df$Manufacturer[i]
# is it already in the list of manufacturers?
f <- which(df.manu$Manufacturer == m)
if (length(f) == 0) {
# not yet -- so add manufacturer with count of 1
cat("new manu: ", m, " ")
newRow <- nrow(df.manu)+1
df.manu[newRow,1] <- 1
df.manu[newRow,2] <- m
cat(newRow, "/", df.manu[newRow,2] <- m, "\n")
} else {
# yes -- bump the count
df.manu[f,2] <- df.manu[f,2] +1
}
}
## new manu: Nabisco 1 / Nabisco
## new manu: Kellogg 2 / Kellogg
## new manu: Kellogg 3 / Kellogg
Note: The “” in cat()
adds a newline, i.e., the next output appears on a new line.
traceback()
The traceback()
function prints the lines of code that were executed just prior to arriving at the error. Programmers often refer to this as a call stack, stack trace or backtrace. When code stops running, programmers often say that is “crashed”, “barfed”, or “dumped core”. The latter harks back to the days when computers had “core” memory made up of vacuum tubes.
You can either directly call traceback()
immediately after the error has occurred, from the console or by inserting the call into your code when you run it again.
df <- read.csv(file = "CerealDataCSV.csv",
header = T,
stringsAsFactors = F)
# total number of cereals
n <- nrow(df)
# new data frame holding manufacturers and count
df.manu <- data.frame(Manufacturer = vector(mode = "character", 0),
NumCereals = vector(mode = "numeric", 0))
for (i in 1:3)
{
# next manufacturer in the data
m <- df$Manufacturer[i]
# is it already in the list of manufacturers?
f <- which(df.manu$Manufacturer == m)
if (length(f) == 0) {
# not yet -- so add manufacturer with count of 1
newRow <- nrow(df.manu)+1
# this next line will cause an error
df.manu[newRow] <- 1
df.manu[newRow] <- m
} else {
# yes -- bump the count
df.manu[f,2] <- df.manu[f,2] +1
}
}
When running code chunks in an R Notebook, the cursor will remain on the line where the error occurred giving you a clue as to where to insert traceback()
.
You can add an automatic call to traceback()
so that you’ll see the stack trace after every error. In R Studio you will need to click on the “Show Traceback” link in the error message.
options(error = traceback)
browser()
trace()
Errata
None collected yet. Let us know.
LS0tDQp0aXRsZTogIkRlYnVnZ2luZyBSIENvZGUiDQpwYXJhbXM6DQogIGNhdGVnb3J5OiA2DQogIG51bWJlcjogMTkxDQogIHRpbWU6IDQ1DQogIGxldmVsOiBiZWdpbm5lcg0KICB0YWdzOiAicixkZWJ1Z2dpbmciDQogIGRlc2NyaXB0aW9uOiAiRGVidWdnaW5nIGlzIGFuIGltcG9ydGFudCBlbGVtZW50IG9mIHNvZnR3YXJlDQogICAgICAgICAgICAgICAgZGV2ZWxvcG1lbnQgYW5kIGRlYnVnZ2luZyBtZXRob2RzIGFuZCB0b29scw0KICAgICAgICAgICAgICAgIGFyZSBpbnRlZ3JhbCB0byBwcm9ncmFtbWluZy4gVGhpcyBsZXNzb24NCiAgICAgICAgICAgICAgICBkZW1vbnN0cmF0ZXMgaG93IHRvIGZpbmQgdGhlIHNvdXJjZSBvZiBlcnJvcnMNCiAgICAgICAgICAgICAgICBpbiBSIGNvZGUuIg0KZGF0ZTogIjxzbWFsbD5gciBTeXMuRGF0ZSgpYDwvc21hbGw+Ig0KYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+Ig0KZW1haWw6ICJtLnNjaGVkbGJhdWVyQG5ldS5lZHUiDQphZmZpbGl0YXRpb246ICJOb3J0aGVhc3Rlcm4gVW5pdmVyc2l0eSINCm91dHB1dDogDQogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICBoaWdobGlnaHQ6IHRhbmdvDQotLS0NCg0KLS0tDQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+Ig0KLS0tDQoNCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQ0KYGBgDQoNCiMjIEludHJvZHVjdGlvbg0KDQpQcm9ncmFtbWluZyBpcyBub3QgZWFzeSBhbmQgZGV2ZWxvcGluZyBjb2RlIGlzIG5ldmVyIGEgbGluZWFyIHBhdGggd2l0aG91dCBwcm9ibGVtcywgcmF0aGVyIGl0IGlzIGEgaGlnaHdheSBsaXR0ZXJlZCB3aXRoIGZydXN0cmF0aW9uLCBhbmdlciwgYW5kIGV2ZW50dWFsIGpveS4gQXNpZGUgZnJvbSBwcm9wZXIgZG9pbmcgYW5hbHlzaXMsIHBsYW5uaW5nIGNhcmVmdWxseSwgbGV2ZXJhZ2luZyBhbmQgYXBwbHlpbmcgcHJvZ3JhbSBhbmQgZGVzaWduIHBhdHRlcm5zLCBhbmQgdXNpbmcgc291bmQgcHJvZ3JhbW1pbmcgcHJhY3RpY2VzLCBtaXN0YWtlcyBzdGlsbCBvY2N1ciBhbmQgImJ1Z3MiIG11c3QgYmUgbG9jYXRlZC4gRGVidWdnaW5nIGlzIHRodXMgZXNzZW50aWFsIGZvciBhbnkgcHJvZ3JhbW1pbmcgZWZmb3J0LCBpbmNsdWRpbmcgcHJvZ3JhbW1pbmcgaW4gUi4NCg0KVGhpcyBsZXNzb24gaW50cm9kdWNlcyBjb21tb24gc3RyYXRlZ2llcyBmb3IgaGVscGluZyBsb2NhdGUgZXJyb3JzIGluIFIgcHJvZ3JhbXMuIE1vc3Qgb2YgdGhlIHByYWN0aWNlcyBzaG93biBhcHBseSBvbmx5IHRvIGFjdHVhbCAicHJvZ3JhbXMiLCAqaS5lLiosIFIgc2NyaXB0cywgYW5kIG1heSBub3Qgd29yayB3ZWxsIGluIFIgTWFya2Rvd24sIFIgTm90ZWJvb2tzLCBvciAqKnNoaW55Kiogd2ViIGFwcHMuIEhvd2V2ZXIsIGNvZGUgY2FuIGJlIGNvcGllZCBmcm9tIGFuIFIgTm90ZWJvb2sgdG8gYW4gUiBTY3JpcHQgZm9yIGRlYnVnZ2luZyBwdXJwb3Nlcy4gVGhlIHJlYXNvbiB3aHkgUiBNYXJrZG93biBpcyBub3QgZWFzaWx5IGRlYnVnZ2VkIGlzIHRoYXQgdGhlIGNvZGUgaXMgcnVuIGR1cmluZyAqa25pdHRpbmcqIHdoZXJlIG91dHB1dCBhbmQgY29kZSBpcyBpbnRlcmNlcHRlZCBieSB0aGUgKmtuaXRyKiBwYWNrYWdlIGNvZGUuDQoNCiMjIENvbW1vbiBTdHJhdGVnaWVzDQoNCiMjIERlYnVnZ2luZw0KDQpUaGlzIHNlY3Rpb24gcHJlc2VudHMgYSBzZXJpZXMgb2YgY29tbW9uIHN0cmF0ZWdpZXMgZm9yIGRlYWxpbmcgd2l0aCBidWdzIGFuZCBlcnJvcnMuDQoNCiMjIyBHb29nbGUNCg0KV2hlbmV2ZXIgeW91IGVuY291bnRlciBhbiBlcnJvciBtZXNzYWdlLCBzdGFydCBieSBzZWFyY2hpbmcgZm9yIHRoZSBtZXNzYWdlIG9ubGluZS4gQ2hhbmNlcyBhcmUgdGhhdCBzb21lb25lIGVsc2UgaGFzIGVuY291bnRlcmVkIHRoaXMgZXJyb3IgYmVmb3JlIGFuZCB0aGVyZSdzIGEgc29sdXRpb24gcG9zdGVkLiBXaGVuICJnb29nbGluZyIsIGltcHJvdmUgdGhlIGxpa2VsaWhvb2Qgb2YgYSBnb29kIG1hdGNoIGJ5IHJlbW92aW5nIGFueSB2YXJpYWJsZSBuYW1lcyBvciB2YWx1ZXMgdGhhdCBhcmUgc3BlY2lmaWMgdG8geW91ciBjb2RlIGFuZCBlbmNsb3NlIHlvdXIgeW91ciBlcnJvciBtZXNzYWdlIGluIHF1b3RlcyB3aGVuIHlvdSB1c2UgaXQgYXMgYSBzZWFyY2ggdGVybS4NCg0KT2YgY291cnNlLCBiZSBzdXJlIHRvIGFja25vd2xlZGdlIHRoZSBzb2x1dGlvbiBpbiB5b3VyIGNvZGUsIHBvc3QgeW91ciBvd24gc29sdXRpb25zLCBhbmQgY29udHJpYnV0ZSB0byBrbm93bGVkZ2UgYmFzaXMgLS0gYXQgbGVhc3QgYnkgImxpa2luZyIgYW4gYW5zd2VyIGlmIGl0IHdhcyBoZWxwZnVsIG9yIGV4cGxhaW5pbmcgd2h5IGFuIGFuc3dlciBkb2VzIG5vdCB3b3JrIG9yIGRpZCBub3Qgd29yayBmb3IgeW91Lg0KDQojIyMgSXNvbGF0ZSBEZWZlY3RpdmUgQ29kZQ0KDQpGaW5kaW5nIGFuIGVycm9yIGluIGEgbGFyZ2UgcHJvZ3JhbSBpcyBuZWFybHkgaW1wb3NzaWJsZS4gU3RhcnQgYnkgZXh0cmFjdGluZyAoaW50byBhIHNlcGFyYXRlIHNvdXJjZSBmaWxlKSB0aGUgY29kZSB0aGF0IGRvZXMgbm90IHdvcmsgYW5kIHJlZHVjZSBhbnkgZGF0YSBzdHJ1Y3R1cmUgb3IgZmlsZXMgdG8gYSBzbWFsbCBzYW1wbGUgdGhhdCBhbGxvd3MgeW91IHRvIHJlcHJvZHVjZSB0aGUgZXJyb3IuDQoNCiMjIyBEZWJ1ZyBpbiBTYW5kYm94DQoNCkRvIHlvdXIgZGVidWdnaW5nIGluIHNlcGFyYXRlIGNvZGUgZmlsZXMuIERvIG5vdCBkZWJ1ZyBpbiB5b3VyIHByb2R1Y3Rpb24gY29kZSBpZiBhdCBhbGwgcG9zc2libGUuIEV4cGVyaW1lbnQgaW4gdGhhdCBzYW5kYm94Lg0KDQojIyMgQWRkIERlYnVnIFN0YXRlbWVudHMNCg0KT25lIG9mIHRoZSBtb3N0IGNvbW1vbiBkZWJ1Z2dpbmcgdHJpY2tzIGlzIHRvIGluc2VydCB2YXJpb3VzIGZvcm1zIG9mIHByaW50IHN0YXRlbWVudHMgaW50byB0aGUgY29kZSB0byBpbmZvcm0gdGhlIHByb2dyYW1tZXIgd2hlcmUgdGhlIHByb2dyYW0gaXMgZXhlY3V0aW5nIGFuZCB3aGF0IHZhbHVlcyBvZiB2YXJpYWJsZXMgYXJlLiBSIHByb3ZpZGVzIG51bWVyb3VzIG1lY2hhbmlzbXMgZm9yIGluc2VydGluZyBkZWJ1ZyBzdGF0ZW1lbnRzIGludG8gUiBjb2RlLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGlzIGFwcHJvYWNoLCBzZWUgdGhlIHNlY3Rpb24gb24gW0RlYnVnZ2luZyBSIFNjcmlwdHNdKCNkZWItci1zY3JpcHRzKSBiZWxvdy4NCg0KIyMjIFVzZSBJbnRlcmFjdGl2ZSBEZWJ1Z2dlcg0KDQpBbiBpbnRlcmFjdGl2ZSBkZWJ1Z2dlciBpcyBvZnRlbiB2ZXJ5IGhlbHBmdWwgaW4gdHJhY2tpbmcgZG93biBlcnJvcnMuIEl0IGFsbG93cyBhIHByb2dyYW1tZXIgdG8gc2V0IGJyZWFrcG9pbnRzLCBpbnNwZWN0IHRoZSBjYWxsIHN0YWNrLCBhbmQgc2VlIHRoZSBjb250ZW50cyBvZiB2YXJpYWJsZXMuDQoNCkZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGhvdyB0byB1c2UgdGhlIGludGVyYWN0aXZlIGRlYnVnZ2VyIGluIFIgU3R1ZGlvLCBzZWUgTGVzc29uIFs2LjE5NSBVc2luZyB0aGUgRGVidWdnZXIgaW4gUiBTdHVkaW9dKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvMDYuci9sLTYtMTk1LXJzdHVkaW8tZGVidWdnZXIvbC02LTE5NS5odG1sKS4NCg0KIyMgRGVidWdnaW5nIFIgU2NyaXB0cyB7I2RlYi1yLXNjcmlwdHN9DQoNClRoZXJlIGFyZSBhIHNldmVyYWwgY29tbW9uIGFwcHJvYWNoZXMgZm9yIGRlYnVnZ2luZyB5b3VyIG93biBjb2RlLiBPZiBjb3Vyc2UsIGNhcmVmdWxseSByZWFkaW5nIHRoZSBlcnJvciBtZXNzYWdlIGFuZCBsaW5lIG51bWJlciB0aGF0IFIgcHJvdmlkZXMgaXMgb2Z0ZW4gYSBmaXJzdCBjbHVlLiBOYXR1cmFsbHksIHRoZSBlcnJvciBjb3VsZCBiZSBvbiB0aGUgbGluZSBwcmlvciBzbyBiZSBzdXJlIHRvIGxvb2sgdGhlcmUgZmlyc3QuDQoNCkluIFIgU3R1ZGlvLCB1c2UgdGhlICpFbnZpcm9ubWVudCogd2luZG93IHRhYiB0byB3YXRjaCB0aGUgdmFsdWUgb2YgdmFyaWFibGVzIGFzIGNvZGUgZXhlY3V0ZXMuDQoNCi0gICBEaXNwbGF5IGRpYWdub3N0aWMgaW5mb3JtYXRpb24sIHZhcmlhYmxlIGNvbnRlbnRzLCBydW4gY291bnRlcnMgYSBjb21iaW5hdGlvbiBvZiBjYWxscyB0byA8Y29kZT5wcmludCgpPC9jb2RlPiwgPGNvZGU+Y2F0KCk8L2NvZGU+IG9yIDxjb2RlPm1lc3NhZ2UoKTwvY29kZT4uDQoNCi0gICBDYWxsIDxjb2RlPnRyYWNlYmFjaygpPC9jb2RlPiB0byBnZXQgYSBzdGFjayB0cmFjZSB0byBzZWUgd2hlcmUgYSByZXBvcnRlZCBlcnJvciBpcyBvY2N1cnJpbmcuDQoNCi0gICBDYWxsIDxjb2RlPmJyb3dzZXIoKTwvY29kZT4gdG8gb3BlbiBhbiBpbnRlcmFjdGl2ZSBkZWJ1Z2dlciB3aW5kb3cNCg0KLSAgIENhbGwgPGNvZGU+ZGVidWcoKTwvY29kZT4gdG8gYXV0b21hdGljYWxseSBvcGVuIGEgZGVidWdnZXIgYXQgdGhlIHN0YXJ0IG9mIGEgZnVuY3Rpb24gY2FsbA0KDQotICAgQ2FsbCA8Y29kZT50cmFjZSgpPC9jb2RlPiB0byBzZXQgYSBicmVha3BvaW50IGJ5IG9wZW5pbmcgYSBkZWJ1Z2dlciBhdCBhIGxvY2F0aW9uIGluc2lkZSBhIGZ1bmN0aW9uLg0KDQojIyBEaWFnbm9zdGljIE1lc3NhZ2VzDQoNCkJlbG93IGlzIGEgc2hvcnQgcGllY2Ugb2YgY29kZSB0aGF0IHJlYWRzIGEgQ1NWIG9mIGNlcmVhbHMsIGNlcmVhbCBjb250ZW50LCBhbmQgbmFtZSBvZiBtYW51ZmFjdHVyZXIgYW5kIHRoZW4gY291bnRzIGhvdyBtYW55IGNlcmVhbHMgZWFjaCBtYW51ZmFjdHVyZXIgcHJvZHVjZXMuIFdlIHdpbGwgbW9kaWZ5IHRoZSBjb2RlIHRvIGRlbW9uc3RyYXRlIHZhcmlvdXMgZGVidWdnaW5nIHByYWN0aWNlcy4NCg0KYGBge3J9DQpkZiA8LSByZWFkLmNzdihmaWxlID0gIkNlcmVhbERhdGFDU1YuY3N2IiwgDQogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgDQogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpDQoNCiMgdG90YWwgbnVtYmVyIG9mIGNlcmVhbHMNCm4gPC0gbnJvdyhkZikNCg0KIyBuZXcgZGF0YSBmcmFtZSBob2xkaW5nIG1hbnVmYWN0dXJlcnMgYW5kIGNvdW50DQpkZi5tYW51IDwtIGRhdGEuZnJhbWUoTWFudWZhY3R1cmVyID0gdmVjdG9yKG1vZGUgPSAiY2hhcmFjdGVyIiwgMCksDQogICAgICAgICAgICAgICAgICAgICAgTnVtQ2VyZWFscyA9IHZlY3Rvcihtb2RlID0gIm51bWVyaWMiLCAwKSkNCg0KZm9yIChpIGluIDE6bikNCnsNCiAgIyBuZXh0IG1hbnVmYWN0dXJlciBpbiB0aGUgZGF0YQ0KICBtIDwtIGRmJE1hbnVmYWN0dXJlcltpXQ0KICANCiAgIyBpcyBpdCBhbHJlYWR5IGluIHRoZSBsaXN0IG9mIG1hbnVmYWN0dXJlcnM/DQogIGYgPC0gd2hpY2goZGYubWFudSRNYW51ZmFjdHVyZXIgPT0gbSkNCiAgaWYgKGxlbmd0aChmKSA9PSAwKSB7DQogICAgIyBub3QgeWV0IC0tIHNvIGFkZCBtYW51ZmFjdHVyZXIgd2l0aCBjb3VudCBvZiAxDQogICAgbmV3Um93IDwtIG5yb3coZGYubWFudSkrMQ0KICAgIGRmLm1hbnVbbmV3Um93LDJdIDwtIDENCiAgICBkZi5tYW51W25ld1JvdywxXSA8LSBtDQogIH0gZWxzZSB7DQogICAgIyB5ZXMgLS0gYnVtcCB0aGUgY291bnQNCiAgICBkZi5tYW51W2YsMl0gPC0gZGYubWFudVtmLDJdICsxDQogIH0NCn0NCg0KcHJpbnQoaGVhZChkZi5tYW51LCAzKSkNCmBgYA0KDQpJZiB5b3UgY29kZSBkb2VzIG5vdCBiZWhhdmUgYXMgZXhwZWN0ZWQsIGEgY29tbW9uIHRhY3RpYyBpcyB0byBhZGQgPGNvZGU+cHJpbnQoKTwvY29kZT4gb3IgPGNvZGU+Y2F0KCk8L2NvZGU+IG1lc3NhZ2VzIGluIHlvdXIgY29kZSBhbmQgcHJpbnQgb3V0IGRpYWdub3N0aWMgbWVzc2FnZXMgdGhhdCBpbmNsdWRlIHZhbHVlcyBvZiB2YXJpYWJsZXMgYW5kIG9iamVjdHMuIFVzZSA8Y29kZT5zdHIoKTwvY29kZT4gdG8gcHJpbnQgbW9yZSBkZXRhaWxzIGFib3V0IGFuIG9iamVjdCdzIHN0cnVjdHVyZS4NCg0KT25lIG1haW4gZHJhd2JhY2sgb2YgdGhpcyBhcHByb2FjaCBpcyB0aGF0IHlvdSBjYW5ub3QgYWRkIDxjb2RlPnByaW50KCk8L2NvZGU+IHRvIGZ1bmN0aW9ucyBvciBsb29wcy4gSW4gYWRkaXRpb24sIGlmIGFkZGVkIHRvIGEgbG9uZyBsb29wLCB0aGUgbWVzc2FnZXMgY2FuIGJlIG92ZXJ3aGVsbWluZy4gRmluYWxseSwgZGlhZ25vc3RpYyBtZXNzYWdlcyB0YWtlIGEgcmVsYXRpdmVseSBsb25nIHRpbWUgdG8gcmVuZGVyIHNvIGNvZGUgd2lsbCBydW4gc2xvd2VyLCBwYXJ0aWN1bGFybHkgbG9uZyBsb29wcy4gSW4gdGhhdCBjYXNlLCByZWR1Y2UgdGhlIG51bWJlciBvZiBpdGVyYXRpb25zIG9mIGEgbG9vcCBvciB1c2UgYSBzdWJzZXQgb2YgYSBkYXRhZnJhbWUgb3IgZmlsZS4NCg0KYGBge3J9DQpkZiA8LSByZWFkLmNzdihmaWxlID0gIkNlcmVhbERhdGFDU1YuY3N2IiwgDQogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgDQogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpDQoNCiMgdG90YWwgbnVtYmVyIG9mIGNlcmVhbHMNCm4gPC0gbnJvdyhkZikNCg0KIyBuZXcgZGF0YSBmcmFtZSBob2xkaW5nIG1hbnVmYWN0dXJlcnMgYW5kIGNvdW50DQpkZi5tYW51IDwtIGRhdGEuZnJhbWUoTWFudWZhY3R1cmVyID0gdmVjdG9yKG1vZGUgPSAiY2hhcmFjdGVyIiwgMCksDQogICAgICAgICAgICAgICAgICAgICAgTnVtQ2VyZWFscyA9IHZlY3Rvcihtb2RlID0gIm51bWVyaWMiLCAwKSkNCg0KY2F0KCJTaXplIG9mIGRmLm1hbnU6ICIsIHN0cihkZi5tYW51KSwgIlxuIikNCg0KZm9yIChpIGluIDE6MykNCnsNCiAgIyBuZXh0IG1hbnVmYWN0dXJlciBpbiB0aGUgZGF0YQ0KICBtIDwtIGRmJE1hbnVmYWN0dXJlcltpXQ0KICANCiAgIyBpcyBpdCBhbHJlYWR5IGluIHRoZSBsaXN0IG9mIG1hbnVmYWN0dXJlcnM/DQogIGYgPC0gd2hpY2goZGYubWFudSRNYW51ZmFjdHVyZXIgPT0gbSkNCiAgaWYgKGxlbmd0aChmKSA9PSAwKSB7DQogICAgIyBub3QgeWV0IC0tIHNvIGFkZCBtYW51ZmFjdHVyZXIgd2l0aCBjb3VudCBvZiAxDQogICAgY2F0KCJuZXcgbWFudTogIiwgbSwgIiAiKQ0KICAgIG5ld1JvdyA8LSBucm93KGRmLm1hbnUpKzENCiAgICBkZi5tYW51W25ld1JvdywxXSA8LSAxDQogICAgZGYubWFudVtuZXdSb3csMl0gPC0gbQ0KICAgIGNhdChuZXdSb3csICIvIiwgZGYubWFudVtuZXdSb3csMl0gPC0gbSwgIlxuIikNCiAgfSBlbHNlIHsNCiAgICAjIHllcyAtLSBidW1wIHRoZSBjb3VudA0KICAgIGRmLm1hbnVbZiwyXSA8LSBkZi5tYW51W2YsMl0gKzENCiAgfQ0KfQ0KYGBgDQoNCioqTm90ZSoqOiBUaGUgIlxuIiBpbiA8Y29kZT5jYXQoKTwvY29kZT4gYWRkcyBhIG5ld2xpbmUsICppLmUuKiwgdGhlIG5leHQgb3V0cHV0IGFwcGVhcnMgb24gYSBuZXcgbGluZS4NCg0KIyMgPGNvZGU+dHJhY2ViYWNrKCk8L2NvZGU+DQoNClRoZSA8Y29kZT50cmFjZWJhY2soKTwvY29kZT4gZnVuY3Rpb24gcHJpbnRzIHRoZSBsaW5lcyBvZiBjb2RlIHRoYXQgd2VyZSBleGVjdXRlZCBqdXN0IHByaW9yIHRvIGFycml2aW5nIGF0IHRoZSBlcnJvci4gUHJvZ3JhbW1lcnMgb2Z0ZW4gcmVmZXIgdG8gdGhpcyBhcyBhIGNhbGwgc3RhY2ssIHN0YWNrIHRyYWNlIG9yIGJhY2t0cmFjZS4gV2hlbiBjb2RlIHN0b3BzIHJ1bm5pbmcsIHByb2dyYW1tZXJzIG9mdGVuIHNheSB0aGF0IGlzICJjcmFzaGVkIiwgImJhcmZlZCIsIG9yICJkdW1wZWQgY29yZSIuIFRoZSBsYXR0ZXIgaGFya3MgYmFjayB0byB0aGUgZGF5cyB3aGVuIGNvbXB1dGVycyBoYWQgImNvcmUiIG1lbW9yeSBtYWRlIHVwIG9mIHZhY3V1bSB0dWJlcy4NCg0KWW91IGNhbiBlaXRoZXIgZGlyZWN0bHkgY2FsbCA8Y29kZT50cmFjZWJhY2soKTwvY29kZT4gaW1tZWRpYXRlbHkgYWZ0ZXIgdGhlIGVycm9yIGhhcyBvY2N1cnJlZCwgZnJvbSB0aGUgY29uc29sZSBvciBieSBpbnNlcnRpbmcgdGhlIGNhbGwgaW50byB5b3VyIGNvZGUgd2hlbiB5b3UgcnVuIGl0IGFnYWluLg0KDQpgYGB7ciBldmFsPUZ9DQpkZiA8LSByZWFkLmNzdihmaWxlID0gIkNlcmVhbERhdGFDU1YuY3N2IiwgDQogICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgDQogICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpDQoNCiMgdG90YWwgbnVtYmVyIG9mIGNlcmVhbHMNCm4gPC0gbnJvdyhkZikNCg0KIyBuZXcgZGF0YSBmcmFtZSBob2xkaW5nIG1hbnVmYWN0dXJlcnMgYW5kIGNvdW50DQpkZi5tYW51IDwtIGRhdGEuZnJhbWUoTWFudWZhY3R1cmVyID0gdmVjdG9yKG1vZGUgPSAiY2hhcmFjdGVyIiwgMCksDQogICAgICAgICAgICAgICAgICAgICAgTnVtQ2VyZWFscyA9IHZlY3Rvcihtb2RlID0gIm51bWVyaWMiLCAwKSkNCg0KZm9yIChpIGluIDE6MykNCnsNCiAgIyBuZXh0IG1hbnVmYWN0dXJlciBpbiB0aGUgZGF0YQ0KICBtIDwtIGRmJE1hbnVmYWN0dXJlcltpXQ0KICANCiAgIyBpcyBpdCBhbHJlYWR5IGluIHRoZSBsaXN0IG9mIG1hbnVmYWN0dXJlcnM/DQogIGYgPC0gd2hpY2goZGYubWFudSRNYW51ZmFjdHVyZXIgPT0gbSkNCiAgaWYgKGxlbmd0aChmKSA9PSAwKSB7DQogICAgIyBub3QgeWV0IC0tIHNvIGFkZCBtYW51ZmFjdHVyZXIgd2l0aCBjb3VudCBvZiAxDQogICAgbmV3Um93IDwtIG5yb3coZGYubWFudSkrMQ0KICAgICAgICANCiAgICAjIHRoaXMgbmV4dCBsaW5lIHdpbGwgY2F1c2UgYW4gZXJyb3INCiAgICBkZi5tYW51W25ld1Jvd10gPC0gMQ0KICAgIGRmLm1hbnVbbmV3Um93XSA8LSBtDQogIH0gZWxzZSB7DQogICAgIyB5ZXMgLS0gYnVtcCB0aGUgY291bnQNCiAgICBkZi5tYW51W2YsMl0gPC0gZGYubWFudVtmLDJdICsxDQogIH0NCn0NCmBgYA0KDQpXaGVuIHJ1bm5pbmcgY29kZSBjaHVua3MgaW4gYW4gUiBOb3RlYm9vaywgdGhlIGN1cnNvciB3aWxsIHJlbWFpbiBvbiB0aGUgbGluZSB3aGVyZSB0aGUgZXJyb3Igb2NjdXJyZWQgZ2l2aW5nIHlvdSBhIGNsdWUgYXMgdG8gd2hlcmUgdG8gaW5zZXJ0IDxjb2RlPnRyYWNlYmFjaygpPC9jb2RlPi4NCg0KWW91IGNhbiBhZGQgYW4gYXV0b21hdGljIGNhbGwgdG8gPGNvZGU+dHJhY2ViYWNrKCk8L2NvZGU+IHNvIHRoYXQgeW91J2xsIHNlZSB0aGUgc3RhY2sgdHJhY2UgYWZ0ZXIgZXZlcnkgZXJyb3IuIEluIFIgU3R1ZGlvIHlvdSB3aWxsIG5lZWQgdG8gY2xpY2sgb24gdGhlICoiU2hvdyBUcmFjZWJhY2siKiBsaW5rIGluIHRoZSBlcnJvciBtZXNzYWdlLg0KDQpgYGB7cn0NCm9wdGlvbnMoZXJyb3IgPSB0cmFjZWJhY2spDQpgYGANCg0KIyMgPGNvZGU+YnJvd3NlcigpPC9jb2RlPg0KDQojIyA8Y29kZT50cmFjZSgpPC9jb2RlPg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgRmlsZXMgJiBSZXNvdXJjZXMNCg0KYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9DQp6aXBOYW1lID0gc3ByaW50ZigiTGVzc29uRmlsZXMtJXMtJXMuemlwIiwgDQogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwNCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikNCg0KdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCANCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikNCg0KIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SDQprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyBTZWUgQWxzbw0KDQpbNi4xOTEgRGVidWdnaW5nIFIgQ29kZV0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wNi5yL2wtNi0xOTEtZGVidWdnaW5nL2wtNi0xOTEuaHRtbCkNCg0KIyMgUmVmZXJlbmNlcw0KDQpbV2lja2hhbSwgSC4gKG4uZC4pIEFkdmFuY2VkIFIuIENoYXB0ZXIgMjI6IERlYnVnZ2luZ10oaHR0cHM6Ly9hZHYtci5oYWRsZXkubnovZGVidWdnaW5nLmh0bWwpDQoNCltEZWJ1Z2dpbmcgUiBDb2RlOiBXaGF0IHRoZXkgRm9yZ290IHRvIFRlYWNoIEFib3V0IFJdKGh0dHBzOi8vcnN0YXRzLnd0Zi9kZWJ1Z2dpbmctci1jb2RlLmh0bWwpDQoNCltNY1BoZXJzb24sIEouICgyMDIxKS4gRGVidWdnaW5nIHdpdGggdGhlIFJTdHVkaW8gSURFLiBEZWMgMjgsIDIwMjEuXShodHRwczovL3N1cHBvcnQucnN0dWRpby5jb20vaGMvZW4tdXMvYXJ0aWNsZXMvMjA1NjEyNjI3LURlYnVnZ2luZy13aXRoLXRoZS1SU3R1ZGlvLUlERSkNCg0KQXZhdGFyIEpvbmF0aGFuIERlY2VtYmVyIDI4LCAyMDIxIDEyOjM0IF0NCg0KIyMgRXJyYXRhDQoNCk5vbmUgY29sbGVjdGVkIHlldC4gTGV0IHVzIGtub3cuDQoNCmBgYHs9aHRtbH0NCjxzY3JpcHQgc3JjPSJodHRwczovL2Zvcm0uam90Zm9ybS5jb20vc3RhdGljL2ZlZWRiYWNrMi5qcyIgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4NCiAgbmV3IEpvdGZvcm1GZWVkYmFjayh7DQogICAgZm9ybUlkOiAiMjEyMTg3MDcyNzg0MTU3IiwNCiAgICBidXR0b25UZXh0OiAiRmVlZGJhY2siLA0KICAgIGJhc2U6ICJodHRwczovL2Zvcm0uam90Zm9ybS5jb20vIiwNCiAgICBiYWNrZ3JvdW5kOiAiI0Y1OTIwMiIsDQogICAgZm9udENvbG9yOiAiI0ZGRkZGRiIsDQogICAgYnV0dG9uU2lkZTogImxlZnQiLA0KICAgIGJ1dHRvbkFsaWduOiAiY2VudGVyIiwNCiAgICB0eXBlOiBmYWxzZSwNCiAgICB3aWR0aDogNzAwLA0KICAgIGhlaWdodDogNTAwLA0KICAgIGlzQ2FyZEZvcm06IGZhbHNlDQogIH0pOw0KPC9zY3JpcHQ+DQpgYGANCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19kZXBsb3lLbml0LlInKSksIGluY2x1ZGUgPSBGQUxTRX0NCmBgYA0K