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==