Chapter 11 Exceptions, Timings and Visibility
Learn how to make your functions throw an error or warning when they get unexpected inputs.
12.1 Exception Handling
Learn how to throw an error or a warning and how to suppress them.
12.1.1 Formal Notifications: Errors and Warnings
An error forces the function to immediately terminate. A warning is less severe. It will allow the function to execute even in an atypical way.
warn_test <-function(x){
if(x<=0){
warning("'x' is less than or equal to 0 but setting it to 1 and continuing")
x <-1
}
return(5/x)
}
error_test <-function(x){
if(x<=0){
stop("'x' is less than or equal to 0 ...Terminating")
}
return(5/x)
}
# trying the functions now.
warn_test(5)
[1] 1
error_test(5)
[1] 1
warn_test(1)
[1] 5
error_test(1)
[1] 5
warn_test(0)
'x' is less than or equal to 0 but setting it to 1 and continuing
[1] 5
error_test(0)
Error in error_test(0) : 'x' is less than or equal to 0 ...Terminating
You can use the print and cat commands in addition to warning and stop for effective debugging.
12.1.2 Catching errors with [try]
When a function terminates from an error, it also terminates any parent functions. To avoid this sever consequence, you can use [try] to attempt a function call and check whether it produces an error.
YOu can also use an [if]statement to specify alternative operations, rather than allowing all process to cease.
myfibrec2 <-function(n){
if(n<0){
warning("Assuming you meant 'n' to be positive --doing that instead")
n<-n*-1
} else if(n==0){
stop("'n' is uninterpretable at o")
}
if(n==1 ||n==2){
return(1)
} else {
return(myfibrec2(n-1)+myfibrec2(n-2))
}
}
# first attempt with errors
attempt1 <-try(myfibrec2(0),silent = TRUE)
attempt1
[1] "Error in myfibrec2(0) : 'n' is uninterpretable at o\n"
attr(,"class")
[1] "try-error"
attr(,"condition")
<simpleError in myfibrec2(0): 'n' is uninterpretable at o>
# second attempt without errors
attempt2 <-try(myfibrec2(6),silent = TRUE)
attempt2
[1] 8
Using [try] in the body of a function
myfibvector <-function(nvec){
nterms <-length(nvec)
result <- rep(0,nterms)
for(i in 1:nterms){
result[i]<-myfibrec2(nvec[i])
}
return(result)
}
# test it out
foo <-myfibvector(nvec=c(1,2,10,8))
foo
[1] 1 1 55 21
However, when one of the elements in the vector is 0, the entire function terminates
foo <-myfibvector(nvec=c(3,2,7,0,9,13))
Error in myfibrec2(nvec[i]) : 'n' is uninterpretable at o
To prevent this, use [try] and [if] to catch the error.
myfibvectorTRY <-function(nvec){
nterms <-length(nvec)
result <- rep(0,nterms)
for(i in 1:nterms){
# insert the try here
attempt <-try(myfibrec2(nvec[i]),silent=T)
if(class(attempt)=="try-error"){
result[i]<-NA
} else {
result[i]<- attempt
}
}
return(result)
}
# now try it out again
foo <-myfibvectorTRY(nvec=c(3,2,7,0,9,13))
foo
[1] 2 1 13 NA 34 233
The [Try] command is a simplification of R’s more complex [tryCatch] function
Suppressing Warning messages Note that the setting silent=TRUE only suppresses error messages. It has no effect on Warning messages.
attempt3 <-try(myfibrec2(-3),silent=TRUE)
Assuming you meant 'n' to be positive --doing that instead
attempt3
[1] 2
If you want to suppress warning messages, use [suppressWarnings]
attempt4<-suppressWarnings(myfibrec2(-3))
attempt4
[1] 2
12.2 Progress and Timing
You sometimes want to have a progress indicator for long running functions.
12.2.1 Textual Progress Bars: Are we there yet?
A progress bar shows how far along R is as it executes a set of operations. To show how this works, you need to run code that takes a while to execute. Try using the Sys.sleep command to make R pause for a specified amount of time, in seconds, before continuing.
This code will run for 3 seconds before you can continue using the console.
Sys.sleep(3)
Consider the following:
sleep_test <- function(n){
result <-0
for(i in 1:n){
result <-result+1
Sys.sleep(0.5)
}
return(result)
}
# call the function
sleep_test(8)
[1] 8
To trak the progress you need to do 3 steps: Initialize the progress bar update the bar terminate the bar with [close]
This code adds progress bar to the code above:
prog_test <- function(n){
result <-0
progbar <-txtProgressBar(min=0,max=n,style=1,char="=")
for(i in 1:n){
result <-result+1
Sys.sleep(0.5)
setTxtProgressBar(progbar,value=i)
}
close(progbar)
return(result)
}
# call the function
prog_test(8)
========================================================================================================
[1] 8
12.2.2 Measuring Completion Time: How long did it take?
Use Sys.time to track the system time. Then its a matter of subtracting start time from end time.
t1<- Sys.time()
Sys.sleep(3)
t2<- Sys.time()
t2-t1
Time difference of 3.392388 secs
For more detailed timings, you can also use [proc.time()] to receive not just the total elapse time but also computer related cpu timings.
To time a single expression, you can also usethe system.time function.
Others include rbenchmark package
12.3 Masking
“Masking” refers to how R will use one object over other similarly named objects.
12.3.1 Function and Object Distinction
You can see the current search path using search() function
search()
[1] ".GlobalEnv" "tools:rstudio" "package:stats" "package:graphics" "package:grDevices"
[6] "package:utils" "package:datasets" "package:methods" "Autoloads" "package:base"
When R searches, the function or object that falls closes to the start of the search path is reached first and used.
Observe the normal [sum] command and a user-defined function [sum]
# normal sum
foo <-c(4,1,5,3)
sum(foo)
[1] 13
# user defined sum function
sum<-function(x){
result<-0
for(i in 1:length(x)){
result <-result+x[i]^2
}
return(result)
}
# call sum function
sum(foo)
[1] 51
The user defined [sum] takes precedence because it is stored in the global environment.
# if you want the base [sum] version to run
base::sum(foo)
[1] 13
Include the name of its package in the call with a double colon. This tells r to sue the version in Base, even though there’s another version of the sum function in the global environment.
To remove the sum function from the Global environment use rm()
rm(sum)
When package objects clash When you load a package, R will notify you if any objects in the package clash with other objects that are accesible in the present session.
library("spatstat")
Loading required package: nlme
Loading required package: rpart
spatstat 1.49-0 (nickname: c$<c81So-Called Softwarec$<c82)
For an introduction to spatstat, type c$<c81beginnerc$<c82
library("car")
Attaching package: c$<c81carc$<c82
The following objects are masked from c$<c81package:spatstatc$<c82:
bc, ellipse
This indicates that two packages each have an object with the same name (ellipse) Both car and spatstats remain completely functional. Using ellipse at the prompt will access car’s object since that package was loaded more recently.
To use spatstats’ version, you must type spatstat::ellipse.
To unmount packages >/b> Use the detach() command
search()
[1] ".GlobalEnv" "package:car" "package:spatstat" "package:rpart" "package:nlme"
[6] "tools:rstudio" "package:stats" "package:graphics" "package:grDevices" "package:utils"
[11] "package:datasets" "package:methods" "Autoloads" "package:base"
# unmount car
detach("package:car", unload=TRUE)
c$<c81carc$<c82 namespace cannot be unloaded:
namespace c$<c81carc$<c82 is imported by c$<c81caretc$<c82 so cannot be unloaded
search()
[1] ".GlobalEnv" "package:spatstat" "package:rpart" "package:nlme" "tools:rstudio"
[6] "package:stats" "package:graphics" "package:grDevices" "package:utils" "package:datasets"
[11] "package:methods" "Autoloads" "package:base"
12.3.2 Data Frame Variable Distinction
You will be explicity notified of masking when you add a data frame to the search path.
foo<-data.frame(surname=c("a","b","c","d"),sex=c(0,1,1,0),height=c(170,168,181,180),
stringsAsFactors = F)
foo
Normally you use $ to access a column in the format foo$surname But you can create a shortcut by attach-ing the variable. and then you can access ‘surname’ without the $
attach(foo)
The following objects are masked from foo (pos = 3):
height, sex, surname
search()
[1] ".GlobalEnv" "foo" "foo" "package:spatstat" "package:rpart"
[6] "package:nlme" "tools:rstudio" "package:stats" "package:graphics" "package:grDevices"
[11] "package:utils" "package:datasets" "package:methods" "Autoloads" "package:base"
foo$surname
[1] "a" "b" "c" "d"
surname
[1] "a" "b" "c" "d"
LS0tDQp0aXRsZTogIkNoYXB0ZXIgMTIgRXhjZXB0aW9ucywgVGltaW5ncyBhbmQgVmlzaWJpbGl0eSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjxoMT4gQ2hhcHRlciAxMSBFeGNlcHRpb25zLCBUaW1pbmdzIGFuZCBWaXNpYmlsaXR5IDwvaDE+DQpMZWFybiBob3cgdG8gbWFrZSB5b3VyIGZ1bmN0aW9ucyB0aHJvdyBhbiBlcnJvciBvciB3YXJuaW5nIHdoZW4gdGhleSBnZXQgdW5leHBlY3RlZCBpbnB1dHMuDQoNCjxoMj4gMTIuMSBFeGNlcHRpb24gSGFuZGxpbmc8L2gyPg0KTGVhcm4gaG93IHRvIHRocm93IGFuIGVycm9yIG9yIGEgd2FybmluZyBhbmQgaG93IHRvIHN1cHByZXNzIHRoZW0uDQoNCjxoMz4gMTIuMS4xIEZvcm1hbCBOb3RpZmljYXRpb25zOiBFcnJvcnMgYW5kIFdhcm5pbmdzIDwvaDM+DQpBbiBlcnJvciBmb3JjZXMgdGhlIGZ1bmN0aW9uIHRvIGltbWVkaWF0ZWx5IHRlcm1pbmF0ZS4NCkEgd2FybmluZyBpcyBsZXNzIHNldmVyZS4gSXQgd2lsbCBhbGxvdyB0aGUgZnVuY3Rpb24gdG8gZXhlY3V0ZSBldmVuIGluIGFuIGF0eXBpY2FsIHdheS4NCg0KYGBge3J9DQp3YXJuX3Rlc3QgPC1mdW5jdGlvbih4KXsNCiAgaWYoeDw9MCl7DQogICAgd2FybmluZygiJ3gnIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAwIGJ1dCBzZXR0aW5nIGl0IHRvIDEgYW5kIGNvbnRpbnVpbmciKQ0KICAgIHggPC0xDQogIH0NCiAgcmV0dXJuKDUveCkNCn0NCg0KZXJyb3JfdGVzdCA8LWZ1bmN0aW9uKHgpew0KICBpZih4PD0wKXsNCiAgICBzdG9wKCIneCcgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDAgLi4uVGVybWluYXRpbmciKQ0KICB9DQogIHJldHVybig1L3gpDQp9DQoNCiMgdHJ5aW5nIHRoZSBmdW5jdGlvbnMgbm93Lg0Kd2Fybl90ZXN0KDUpDQplcnJvcl90ZXN0KDUpDQoNCndhcm5fdGVzdCgxKQ0KZXJyb3JfdGVzdCgxKQ0KDQp3YXJuX3Rlc3QoMCkNCmVycm9yX3Rlc3QoMCkNCg0KYGBgDQpZb3UgY2FuIHVzZSB0aGUgcHJpbnQgYW5kIGNhdCBjb21tYW5kcyBpbiBhZGRpdGlvbiB0byB3YXJuaW5nIGFuZCBzdG9wIGZvciBlZmZlY3RpdmUgZGVidWdnaW5nLg0KDQoNCjxoMz4gMTIuMS4yIENhdGNoaW5nIGVycm9ycyB3aXRoIFt0cnldIDwvaDM+DQpXaGVuIGEgZnVuY3Rpb24gdGVybWluYXRlcyBmcm9tIGFuIGVycm9yLCBpdCBhbHNvIHRlcm1pbmF0ZXMgYW55IHBhcmVudCBmdW5jdGlvbnMuIFRvIGF2b2lkIHRoaXMgc2V2ZXIgY29uc2VxdWVuY2UsIHlvdSBjYW4gdXNlIFt0cnldIHRvIGF0dGVtcHQgYSBmdW5jdGlvbiBjYWxsIGFuZCBjaGVjayB3aGV0aGVyIGl0IHByb2R1Y2VzIGFuIGVycm9yLiANCg0KWU91IGNhbiBhbHNvIHVzZSBhbiBbaWZdc3RhdGVtZW50IHRvIHNwZWNpZnkgYWx0ZXJuYXRpdmUgb3BlcmF0aW9ucywgcmF0aGVyIHRoYW4gYWxsb3dpbmcgYWxsIHByb2Nlc3MgdG8gY2Vhc2UuDQoNCmBgYHtyfQ0KDQpteWZpYnJlYzIgPC1mdW5jdGlvbihuKXsNCiAgaWYobjwwKXsNCiAgICB3YXJuaW5nKCJBc3N1bWluZyB5b3UgbWVhbnQgJ24nIHRvIGJlIHBvc2l0aXZlIC0tZG9pbmcgdGhhdCBpbnN0ZWFkIikNCiAgICBuPC1uKi0xDQogIH0gZWxzZSBpZihuPT0wKXsNCiAgICAgIHN0b3AoIiduJyBpcyB1bmludGVycHJldGFibGUgYXQgbyIpDQogIH0NCiAgaWYobj09MSB8fG49PTIpew0KICAgIHJldHVybigxKQ0KICB9IGVsc2Ugew0KICAgICAgIHJldHVybihteWZpYnJlYzIobi0xKStteWZpYnJlYzIobi0yKSkNCiAgICB9DQp9DQogIA0KIyBmaXJzdCBhdHRlbXB0IHdpdGggZXJyb3JzICANCmF0dGVtcHQxIDwtdHJ5KG15ZmlicmVjMigwKSxzaWxlbnQgPSBUUlVFKQ0KYXR0ZW1wdDENCg0KIyBzZWNvbmQgYXR0ZW1wdCB3aXRob3V0IGVycm9ycw0KYXR0ZW1wdDIgPC10cnkobXlmaWJyZWMyKDYpLHNpbGVudCA9IFRSVUUpDQphdHRlbXB0Mg0KDQoNCmBgYA0KDQpVc2luZyBbdHJ5XSBpbiB0aGUgYm9keSBvZiBhIGZ1bmN0aW9uDQoNCmBgYHtyfQ0KbXlmaWJ2ZWN0b3IgPC1mdW5jdGlvbihudmVjKXsNCiAgbnRlcm1zIDwtbGVuZ3RoKG52ZWMpDQogIHJlc3VsdCA8LSByZXAoMCxudGVybXMpDQogIGZvcihpIGluIDE6bnRlcm1zKXsNCiAgICByZXN1bHRbaV08LW15ZmlicmVjMihudmVjW2ldKQ0KICB9DQogIHJldHVybihyZXN1bHQpDQp9DQoNCiMgdGVzdCBpdCBvdXQNCmZvbyA8LW15ZmlidmVjdG9yKG52ZWM9YygxLDIsMTAsOCkpDQpmb28NCg0KYGBgDQoNCkhvd2V2ZXIsIHdoZW4gb25lIG9mIHRoZSBlbGVtZW50cyBpbiB0aGUgdmVjdG9yIGlzIDAsIHRoZSBlbnRpcmUgZnVuY3Rpb24gdGVybWluYXRlcw0KDQpgYGB7cn0NCmZvbyA8LW15ZmlidmVjdG9yKG52ZWM9YygzLDIsNywwLDksMTMpKQ0KZm9vDQoNCmBgYA0KDQpUbyBwcmV2ZW50IHRoaXMsIHVzZSBbdHJ5XSBhbmQgW2lmXSB0byBjYXRjaCB0aGUgZXJyb3IuDQoNCmBgYHtyfQ0KbXlmaWJ2ZWN0b3JUUlkgPC1mdW5jdGlvbihudmVjKXsNCiAgbnRlcm1zIDwtbGVuZ3RoKG52ZWMpDQogIHJlc3VsdCA8LSByZXAoMCxudGVybXMpDQogIGZvcihpIGluIDE6bnRlcm1zKXsNCiAgICAjIGluc2VydCB0aGUgdHJ5IGhlcmUNCiAgICBhdHRlbXB0IDwtdHJ5KG15ZmlicmVjMihudmVjW2ldKSxzaWxlbnQ9VCkNCiAgICBpZihjbGFzcyhhdHRlbXB0KT09InRyeS1lcnJvciIpew0KICAgICAgcmVzdWx0W2ldPC1OQQ0KICAgIH0gZWxzZSB7DQogICAgICByZXN1bHRbaV08LSBhdHRlbXB0DQogICAgfQ0KICB9DQogIHJldHVybihyZXN1bHQpDQp9DQoNCg0KIyBub3cgdHJ5IGl0IG91dCBhZ2Fpbg0KZm9vIDwtbXlmaWJ2ZWN0b3JUUlkobnZlYz1jKDMsMiw3LDAsOSwxMykpDQpmb28NCmBgYA0KVGhlIFtUcnldIGNvbW1hbmQgaXMgYSBzaW1wbGlmaWNhdGlvbiBvZiBSJ3MgbW9yZSBjb21wbGV4IFt0cnlDYXRjaF0gZnVuY3Rpb24NCg0KPGI+IFN1cHByZXNzaW5nIFdhcm5pbmcgbWVzc2FnZXM8L2I+DQpOb3RlIHRoYXQgdGhlIHNldHRpbmcgc2lsZW50PVRSVUUgb25seSBzdXBwcmVzc2VzIGVycm9yIG1lc3NhZ2VzLiBJdCBoYXMgbm8gZWZmZWN0IG9uIFdhcm5pbmcgbWVzc2FnZXMuDQoNCmBgYHtyfQ0KYXR0ZW1wdDMgPC10cnkobXlmaWJyZWMyKC0zKSxzaWxlbnQ9VFJVRSkNCmF0dGVtcHQzDQpgYGANCg0KSWYgeW91IHdhbnQgdG8gc3VwcHJlc3Mgd2FybmluZyBtZXNzYWdlcywgdXNlIFtzdXBwcmVzc1dhcm5pbmdzXQ0KDQpgYGB7cn0NCmF0dGVtcHQ0PC1zdXBwcmVzc1dhcm5pbmdzKG15ZmlicmVjMigtMykpDQphdHRlbXB0NA0KDQpgYGANCg0KDQo8aDI+IDEyLjIgUHJvZ3Jlc3MgYW5kIFRpbWluZzwvaDI+DQpZb3Ugc29tZXRpbWVzIHdhbnQgdG8gaGF2ZSBhIHByb2dyZXNzIGluZGljYXRvciBmb3IgbG9uZyBydW5uaW5nIGZ1bmN0aW9ucy4NCg0KPGgzPiAxMi4yLjEgVGV4dHVhbCBQcm9ncmVzcyBCYXJzOiBBcmUgd2UgdGhlcmUgeWV0PyA8L2gzPg0KQSBwcm9ncmVzcyBiYXIgc2hvd3MgaG93IGZhciBhbG9uZyBSIGlzIGFzIGl0IGV4ZWN1dGVzIGEgc2V0IG9mIG9wZXJhdGlvbnMuIFRvIHNob3cgaG93IHRoaXMgd29ya3MsIHlvdSBuZWVkIHRvIHJ1biBjb2RlIHRoYXQgdGFrZXMgYSB3aGlsZSB0byBleGVjdXRlLiAgVHJ5IHVzaW5nIHRoZSBTeXMuc2xlZXAgY29tbWFuZCB0byBtYWtlIFIgcGF1c2UgZm9yIGEgc3BlY2lmaWVkIGFtb3VudCBvZiB0aW1lLCBpbiBzZWNvbmRzLCBiZWZvcmUgY29udGludWluZy4NCg0KVGhpcyBjb2RlIHdpbGwgcnVuIGZvciAzIHNlY29uZHMgYmVmb3JlIHlvdSBjYW4gY29udGludWUgdXNpbmcgdGhlIGNvbnNvbGUuIA0KDQpgYGB7cn0NClN5cy5zbGVlcCgzKQ0KDQpgYGANCg0KQ29uc2lkZXIgdGhlIGZvbGxvd2luZzoNCg0KYGBge3J9DQpzbGVlcF90ZXN0IDwtIGZ1bmN0aW9uKG4pew0KICByZXN1bHQgPC0wDQogIGZvcihpIGluIDE6bil7DQogICAgcmVzdWx0IDwtcmVzdWx0KzENCiAgICBTeXMuc2xlZXAoMC41KQ0KICB9DQogIHJldHVybihyZXN1bHQpDQp9DQoNCiMgY2FsbCB0aGUgZnVuY3Rpb24gDQpzbGVlcF90ZXN0KDgpDQoNCmBgYA0KDQpUbyB0cmFrIHRoZSBwcm9ncmVzcyB5b3UgbmVlZCB0byBkbyAzIHN0ZXBzOg0KSW5pdGlhbGl6ZSB0aGUgcHJvZ3Jlc3MgYmFyDQp1cGRhdGUgdGhlIGJhcg0KdGVybWluYXRlIHRoZSBiYXIgd2l0aCBbY2xvc2VdDQoNClRoaXMgY29kZSBhZGRzIHByb2dyZXNzIGJhciB0byB0aGUgY29kZSBhYm92ZToNCg0KYGBge3J9DQpwcm9nX3Rlc3QgPC0gZnVuY3Rpb24obil7DQogIHJlc3VsdCA8LTANCiAgcHJvZ2JhciA8LXR4dFByb2dyZXNzQmFyKG1pbj0wLG1heD1uLHN0eWxlPTEsY2hhcj0iPSIpDQogIGZvcihpIGluIDE6bil7DQogICAgcmVzdWx0IDwtcmVzdWx0KzENCiAgICBTeXMuc2xlZXAoMC41KQ0KICAgIHNldFR4dFByb2dyZXNzQmFyKHByb2diYXIsdmFsdWU9aSkNCiAgfQ0KICBjbG9zZShwcm9nYmFyKQ0KICByZXR1cm4ocmVzdWx0KQ0KfQ0KDQojIGNhbGwgdGhlIGZ1bmN0aW9uIA0KcHJvZ190ZXN0KDgpDQpgYGANCiANCjxoMz4gMTIuMi4yIE1lYXN1cmluZyBDb21wbGV0aW9uIFRpbWU6IEhvdyBsb25nIGRpZCBpdCB0YWtlPyA8L2gzPg0KVXNlIFN5cy50aW1lIHRvIHRyYWNrIHRoZSBzeXN0ZW0gdGltZS4gVGhlbiBpdHMgYSBtYXR0ZXIgb2Ygc3VidHJhY3Rpbmcgc3RhcnQgdGltZSBmcm9tIGVuZCB0aW1lLiANCg0KYGBge3J9DQp0MTwtIFN5cy50aW1lKCkNClN5cy5zbGVlcCgzKQ0KdDI8LSBTeXMudGltZSgpDQp0Mi10MQ0KDQpgYGANCkZvciBtb3JlIGRldGFpbGVkIHRpbWluZ3MsIHlvdSBjYW4gYWxzbyB1c2UgW3Byb2MudGltZSgpXSB0byByZWNlaXZlIG5vdCBqdXN0IHRoZSB0b3RhbCBlbGFwc2UgdGltZSBidXQgYWxzbyBjb21wdXRlciByZWxhdGVkIGNwdSB0aW1pbmdzLg0KDQpUbyB0aW1lIGEgc2luZ2xlIGV4cHJlc3Npb24sIHlvdSBjYW4gYWxzbyB1c2V0aGUgc3lzdGVtLnRpbWUgZnVuY3Rpb24uICANCk90aGVycyBpbmNsdWRlIHJiZW5jaG1hcmsgcGFja2FnZQ0KDQo8aDI+IDEyLjMgTWFza2luZyA8L2gyPg0KIk1hc2tpbmciIHJlZmVycyB0byBob3cgUiB3aWxsIHVzZSBvbmUgb2JqZWN0IG92ZXIgb3RoZXIgc2ltaWxhcmx5IG5hbWVkIG9iamVjdHMuDQoNCjxoMz4gMTIuMy4xIEZ1bmN0aW9uIGFuZCBPYmplY3QgRGlzdGluY3Rpb248L2gzPg0KWW91IGNhbiBzZWUgdGhlIGN1cnJlbnQgc2VhcmNoIHBhdGggdXNpbmcgc2VhcmNoKCkgZnVuY3Rpb24NCg0KYGBge3J9DQpzZWFyY2goKQ0KYGBgDQoNCldoZW4gUiBzZWFyY2hlcywgdGhlIGZ1bmN0aW9uIG9yIG9iamVjdCB0aGF0IGZhbGxzIGNsb3NlcyB0byB0aGUgc3RhcnQgb2YgdGhlIHNlYXJjaCBwYXRoIGlzIHJlYWNoZWQgZmlyc3QgYW5kIHVzZWQuIA0KDQpPYnNlcnZlIHRoZSBub3JtYWwgW3N1bV0gY29tbWFuZCBhbmQgYSB1c2VyLWRlZmluZWQgZnVuY3Rpb24gW3N1bV0NCg0KYGBge3J9DQojIG5vcm1hbCBzdW0gDQpmb28gPC1jKDQsMSw1LDMpDQpzdW0oZm9vKQ0KYGBgDQoNCmBgYHtyfQ0KIyB1c2VyIGRlZmluZWQgc3VtIGZ1bmN0aW9uDQpzdW08LWZ1bmN0aW9uKHgpew0KICByZXN1bHQ8LTANCiAgZm9yKGkgaW4gMTpsZW5ndGgoeCkpew0KICAgIHJlc3VsdCA8LXJlc3VsdCt4W2ldXjINCiAgfQ0KICByZXR1cm4ocmVzdWx0KQ0KfQ0KDQojIGNhbGwgc3VtIGZ1bmN0aW9uDQpzdW0oZm9vKQ0KYGBgDQoNClRoZSB1c2VyIGRlZmluZWQgW3N1bV0gdGFrZXMgcHJlY2VkZW5jZSBiZWNhdXNlIGl0IGlzIHN0b3JlZCBpbiB0aGUgZ2xvYmFsIGVudmlyb25tZW50Lg0KDQpgYGB7cn0NCiMgaWYgeW91IHdhbnQgdGhlIGJhc2UgW3N1bV0gdmVyc2lvbiB0byBydW4NCmJhc2U6OnN1bShmb28pDQoNCg0KYGBgDQpJbmNsdWRlIHRoZSBuYW1lIG9mIGl0cyBwYWNrYWdlIGluIHRoZSBjYWxsIHdpdGggYSBkb3VibGUgY29sb24uIFRoaXMgdGVsbHMgciB0byBzdWUgdGhlIHZlcnNpb24gaW4gQmFzZSwgZXZlbiB0aG91Z2ggdGhlcmUncyBhbm90aGVyIHZlcnNpb24gb2YgdGhlIHN1bSBmdW5jdGlvbiBpbiB0aGUgZ2xvYmFsIGVudmlyb25tZW50Lg0KDQpUbyByZW1vdmUgdGhlIHN1bSBmdW5jdGlvbiBmcm9tIHRoZSBHbG9iYWwgZW52aXJvbm1lbnQNCnVzZSBybSgpDQoNCmBgYHtyfQ0Kcm0oc3VtKQ0KDQpgYGANCg0KPGI+V2hlbiBwYWNrYWdlIG9iamVjdHMgY2xhc2g8L2I+DQpXaGVuIHlvdSBsb2FkIGEgcGFja2FnZSwgUiB3aWxsIG5vdGlmeSB5b3UgaWYgYW55IG9iamVjdHMgaW4gdGhlIHBhY2thZ2UgY2xhc2ggd2l0aCBvdGhlciBvYmplY3RzIHRoYXQgYXJlIGFjY2VzaWJsZSBpbiB0aGUgcHJlc2VudCBzZXNzaW9uLiANCg0KYGBge3J9DQpsaWJyYXJ5KCJzcGF0c3RhdCIpDQpsaWJyYXJ5KCJjYXIiKQ0KYGBgDQpUaGlzIGluZGljYXRlcyB0aGF0IHR3byBwYWNrYWdlcyBlYWNoIGhhdmUgYW4gb2JqZWN0IHdpdGggdGhlIHNhbWUgbmFtZSAoZWxsaXBzZSkNCkJvdGggY2FyIGFuZCBzcGF0c3RhdHMgcmVtYWluIGNvbXBsZXRlbHkgZnVuY3Rpb25hbC4NClVzaW5nIGVsbGlwc2UgYXQgdGhlIHByb21wdCB3aWxsIGFjY2VzcyBjYXIncyBvYmplY3Qgc2luY2UgdGhhdCBwYWNrYWdlIHdhcyBsb2FkZWQgbW9yZSByZWNlbnRseS4gDQoNClRvIHVzZSBzcGF0c3RhdHMnIHZlcnNpb24sIHlvdSBtdXN0IHR5cGUgc3BhdHN0YXQ6OmVsbGlwc2UuDQoNCjxiPlRvIHVubW91bnQgcGFja2FnZXMgPi9iPg0KVXNlIHRoZSBkZXRhY2goKSBjb21tYW5kDQoNCmBgYHtyfQ0Kc2VhcmNoKCkNCiMgdW5tb3VudCBjYXINCmRldGFjaCgicGFja2FnZTpjYXIiLCB1bmxvYWQ9VFJVRSkNCnNlYXJjaCgpDQoNCmBgYA0KDQoNCg0KDQoNCjxoMz4gMTIuMy4yIERhdGEgRnJhbWUgVmFyaWFibGUgRGlzdGluY3Rpb248L2gzPg0KWW91IHdpbGwgYmUgZXhwbGljaXR5IG5vdGlmaWVkIG9mIG1hc2tpbmcgd2hlbiB5b3UgYWRkIGEgZGF0YSBmcmFtZSB0byB0aGUgc2VhcmNoIHBhdGguDQoNCmBgYHtyfQ0KZm9vPC1kYXRhLmZyYW1lKHN1cm5hbWU9YygiYSIsImIiLCJjIiwiZCIpLHNleD1jKDAsMSwxLDApLGhlaWdodD1jKDE3MCwxNjgsMTgxLDE4MCksDQogICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpDQpmb28NCg0KYGBgDQpOb3JtYWxseSB5b3UgdXNlICQgdG8gYWNjZXNzIGEgY29sdW1uIGluIHRoZSBmb3JtYXQgZm9vJHN1cm5hbWUNCkJ1dCB5b3UgY2FuIGNyZWF0ZSBhIHNob3J0Y3V0IGJ5IGF0dGFjaC1pbmcgdGhlIHZhcmlhYmxlLg0KYW5kIHRoZW4geW91IGNhbiBhY2Nlc3MgJ3N1cm5hbWUnIHdpdGhvdXQgdGhlICQNCg0KYGBge3J9DQphdHRhY2goZm9vKQ0Kc2VhcmNoKCkNCg0KZm9vJHN1cm5hbWUNCnN1cm5hbWUNCg0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==