Special Values, Classes and Coercion

Special Values

Infinity

Occurs when a number is too large for R to represent. This inf is case sensitive. It can only be associated with numeric values

foo<-Inf
foo
[1] Inf
bar<-c(3401,Inf,3.1,-555,Inf,43)
bar
[1] 3401.0    Inf    3.1 -555.0    Inf   43.0
bar<-90000^100
bar
[1] Inf
# example of negative Inf
qux <-c(42,-Inf,-Inf,Inf,-45623.3)
qux
[1]     42.0     -Inf     -Inf      Inf -45623.3

Note, you can calculate using Inf for example

Inf* -8
[1] -Inf

YOu will see that multiplying Inf with a negative number produces a negative Inf. If you add or multiply infinity you also get infinity as a result

Inf+1
[1] Inf
4*-Inf
[1] -Inf
-45.2-Inf
[1] -Inf
Inf-45.2
[1] Inf
Inf+Inf
[1] Inf
Inf/23
[1] Inf

Zero and infinity go hand in hand. Any finite number dived by infinity will result in zero.

-59/Inf
[1] 0
59/Inf
[1] 0
-59/-Inf
[1] 0
59/-Inf
[1] 0

Any number divided by zero becomes Inf

56/0
[1] Inf
-59/0
[1] -Inf

To detect Inf values, use is.infinite or is.finite

# see the contents of qux
qux
[1]     42.0     -Inf     -Inf      Inf -45623.3
# test for inf values
is.infinite(x=qux)
[1] FALSE  TRUE  TRUE  TRUE FALSE
# test for finite values
is.finite(x=qux)
[1]  TRUE FALSE FALSE FALSE  TRUE

Notice that these functions does not test for either +Inf or -Inf. Finally relational operators work on Inf values

qux
[1]     42.0     -Inf     -Inf      Inf -45623.3
-Inf<Inf
[1] TRUE
Inf>-Inf
[1] TRUE
qux==Inf
[1] FALSE FALSE FALSE  TRUE FALSE
qux==-Inf
[1] FALSE  TRUE  TRUE FALSE FALSE

Nan Not a number

Used when it is impossible to express the result of a calculation using a number, Inf, or -Inf

foo<-NaN
foo
[1] NaN
bar<-c(NaN,54.3,-2,NaN,90094.123,-Inf,55)
bar
[1]      NaN    54.30    -2.00      NaN 90094.12     -Inf    55.00

Whenever you attempt to cancel Inf in any way, the result will be Nan

-Inf+Inf
[1] NaN
Inf/Inf
[1] NaN

Note that any matheatical operation involving NaN will simply result in Nan

NaN+1
[1] NaN
2+6*(4-4)/0
[1] NaN
3.5^(-Inf/Inf)
[1] NaN

Functions to detect Nan : Is.NaN

bar
[1]      NaN    54.30    -2.00      NaN 90094.12     -Inf    55.00
is.nan(x=bar)
[1]  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE
!is.nan(x=bar)
[1] FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
is.nan(x=bar)| is.infinite(x=bar)
[1]  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE
bar[-which(is.nan(x=bar)|is.infinite(x=bar))]
[1]    54.30    -2.00 90094.12    55.00

NOte: The last line uses WHICH command to convert these logical values into numberc index positions so that you can remove them with the negative indexes in the square brackets.

Na Not Available

NA is used for missing values in data sets. Whereas NaN is only used for numeric values, NA can also be used for both numeric and non-numberic settings.

foo<-c("character","a",NA,"with","string",NA)
foo
[1] "character" "a"         NA          "with"      "string"    NA         
bar<-factor(c("blue",NA,NA,"blue","green","blue",NA,"red","red",NA,"green"))
bar
 [1] blue  <NA>  <NA>  blue  green blue  <NA>  red   red   <NA>  green
Levels: blue green red
baz <-matrix(c(1:3,NA,5,6,NA,8,NA),nrow=3,ncol=3)
baz
     [,1] [,2] [,3]
[1,]    1   NA   NA
[2,]    2    5    8
[3,]    3    6   NA

Use Is.na to detect NA elements

baz
     [,1] [,2] [,3]
[1,]    1   NA   NA
[2,]    2    5    8
[3,]    3    6   NA
is.na(x=baz)
      [,1]  [,2]  [,3]
[1,] FALSE  TRUE  TRUE
[2,] FALSE FALSE FALSE
[3,] FALSE FALSE  TRUE
# is.na also flags the NaN values
qux<-c(NA,5.89,Inf,NA,9.43,-2.35,NaN,2.10,-8.53,-7.58,NA,-4.58,2.01,NaN)
qux
 [1]    NA  5.89   Inf    NA  9.43 -2.35   NaN  2.10 -8.53 -7.58    NA -4.58  2.01   NaN
is.na(x=qux)
 [1]  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE

If you want to identify Na and NaN separately

qux
 [1]    NA  5.89   Inf    NA  9.43 -2.35   NaN  2.10 -8.53 -7.58    NA -4.58  2.01   NaN
which(x=is.nan(x=qux))
[1]  7 14

If you want to identify NaN values specifically:

qux
 [1]    NA  5.89   Inf    NA  9.43 -2.35   NaN  2.10 -8.53 -7.58    NA -4.58  2.01   NaN
which(x=(is.na(x=qux)&!is.nan(x=qux)))
[1]  1  4 11

After locating the offending elements, you an use negative indexes in square brackets to remove them. Another way in R is to use the na.omit. It will take a structure and delete all NAs from it. na.omit will also apply to NaNs if the elements are numeric

qux
 [1]    NA  5.89   Inf    NA  9.43 -2.35   NaN  2.10 -8.53 -7.58    NA -4.58  2.01   NaN
quux<-na.omit(object=qux)
quux
[1]  5.89   Inf  9.43 -2.35  2.10 -8.53 -7.58 -4.58  2.01
attr(,"na.action")
[1]  1  4  7 11 14
attr(,"class")
[1] "omit"

Null

Used to EXPLICITLY define an empty entity. Notice there is NO index position for NULL values

foo <- NULL
foo
NULL
bar <- NA
bar
[1] NA
c(2,4,NA,8)
[1]  2  4 NA  8
c(2,4,NULL,8)
[1] 2 4 8

The line using NA has 4 elements, while the line using NULL has only 3 elements Null cannot take up a position in the vector.

The is.null function is used to check when an element is explicitly NULL.

opt.arg <-c("string1","string2","string3")
opt.arg
[1] "string1" "string2" "string3"
is.na(x=opt.arg)
[1] FALSE FALSE FALSE
is.null(x=opt.arg)
[1] FALSE

If the argument is empty using NULL over NA for the check is better.

opt.arg<-c(NA,NA,NA)
is.na(x=opt.arg)
[1] TRUE TRUE TRUE
opt.arg<-c(NULL,NULL,NULL)
is.null(x=opt.arg)
[1] TRUE
opt.arg<-c(NULL,NULL,1)
is.null(x=opt.arg)
[1] FALSE
opt.arg<-c(NULL,NULL,NA)
is.null(x=opt.arg)
[1] FALSE

Null dominates any arithmetic, even if it includes other special values

NULL+53
numeric(0)
53<NULL
logical(0)
NaN-NULL+NA/Inf
numeric(0)

Understanding Types, Classes and Coercion

Attributes

Attributes can be implicit or explicit.

foo<-matrix(data=1:9,nrow=3,ncol=3)
foo
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
attributes(foo)
$dim
[1] 3 3

You can also use this:

attr(x=foo,which="dim")
[1] 3 3

When additional attributes are created, you can see all with the attributes command In this example, we added dimnames to the matrix.

foo<-matrix(data=1:9,nrow=3,ncol=3,dimnames=list(c("a","b","c"),c("d","e","f")))
foo
  d e f
a 1 4 7
b 2 5 8
c 3 6 9
attributes(foo)
$dim
[1] 3 3

$dimnames
$dimnames[[1]]
[1] "a" "b" "c"

$dimnames[[2]]
[1] "d" "e" "f"

We can extract the values of dimnames as well

dimnames(foo)
[[1]]
[1] "a" "b" "c"

[[2]]
[1] "d" "e" "f"

we can also assign an object after it has been created.

foo
  d e f
a 1 4 7
b 2 5 8
c 3 6 9
bar<-list(c("a","b","c"),c("d","e","f"))
bar
[[1]]
[1] "a" "b" "c"

[[2]]
[1] "d" "e" "f"

Object Class

Every object we create is identified either implicitly or explicitly whith at least one class.

num.vec1<-1:4
num.vec1
[1] 1 2 3 4
num.vec2<-seq(from=1, to=4,length=6)
num.vec2
[1] 1.0 1.6 2.2 2.8 3.4 4.0
char.vec<-c("a","few","strings","here")
char.vec
[1] "a"       "few"     "strings" "here"   
logic.vec<-c(T,F,F,F,T,F,T,T)
fac.vec<-factor(c("Blue","Blue","Green","Red","Green","Yellow"))
fac.vec
[1] Blue   Blue   Green  Red    Green  Yellow
Levels: Blue Green Red Yellow

Standalone vectors

class(num.vec1)
[1] "integer"
class(num.vec2)
[1] "numeric"
class(char.vec)
[1] "character"
class(logic.vec)
[1] "logical"
class(fac.vec)
[1] "factor"

Other Data Structures

num.vec1
[1] 1 2 3 4
num.mat1<-matrix(data=num.vec1,nrow=2,ncol=2)
num.mat1
     [,1] [,2]
[1,]    1    3
[2,]    2    4
class(num.mat1)
[1] "matrix"
num.vec2
[1] 1.0 1.6 2.2 2.8 3.4 4.0
num.mat2<-matrix(data=num.vec2,nrow=2,ncol=3)
num.mat2
     [,1] [,2] [,3]
[1,]  1.0  2.2  3.4
[2,]  1.6  2.8  4.0
class(num.mat2)
[1] "matrix"
char.vec
[1] "a"       "few"     "strings" "here"   
char.mat<-matrix(data=char.vec,nrow=2,ncol=2)
char.mat
     [,1]  [,2]     
[1,] "a"   "strings"
[2,] "few" "here"   
class(char.mat)
[1] "matrix"
logic.vec
[1]  TRUE FALSE FALSE FALSE  TRUE FALSE  TRUE  TRUE
logic.mat<-matrix(data=logic.vec,nrow=4,ncol=2)
logic.mat
      [,1]  [,2]
[1,]  TRUE  TRUE
[2,] FALSE FALSE
[3,] FALSE  TRUE
[4,] FALSE  TRUE
class(logic.mat)
[1] "matrix"

Multiple Classes Certain objects will have multiple classes. A variant on a standard form of an object such an ordered factor vector, will inherit the usual factor class and also contain the additional ordered class. Both are returned if you use the class function

ordfac.vec<-factor(x=c("Small","Large","Large","Regular","Small"),levels=c("Small","Regular","Large"),ordered=TRUE)
ordfac.vec
[1] Small   Large   Large   Regular Small  
Levels: Small < Regular < Large
class(ordfac.vec)
[1] "ordered" "factor" 

Is-Dot Checking Functions

To check whether the object is a specific class or data type, you can use the “is-dot function” on the object and it will return TRUE or False logical value.

num.vec1 <-1:4
num.vec1
[1] 1 2 3 4
is.integer(num.vec1)
[1] TRUE
is.numeric(num.vec1)
[1] TRUE
is.matrix(num.vec1)
[1] FALSE
is.data.frame(num.vec1)
[1] FALSE
is.vector(num.vec1)
[1] TRUE
is.logical(num.vec1)
[1] FALSE

AS-Dot Coercion Functions

Coercion is the conversion of one data type to another. Implicit coercion occurs naturally when elements need to be converted to another type in order for an operation to complete. True or false are implicitly coerced into 1 for true and 0 for false.

1:4+c(T,F,F,T)
[1] 2 2 3 5

Another example of coercion when you paste and cat to glue together character strings. Non-character strings are automatically coerced into strings before the concatenation takes places.

foo <-34
bar<-T
paste("Definitely foo:", foo, "; definitely bar: ",bar,".",sep=" ")
[1] "Definitely foo: 34 ; definitely bar:  TRUE ."
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2sgQ2hhcHRlciA2Ig0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogam91cm5hbA0KICAgIHRvYzogeWVzDQotLS0NCg0KPGgxPiBTcGVjaWFsIFZhbHVlcywgQ2xhc3NlcyBhbmQgQ29lcmNpb24gPC9oMT4NCg0KPGgyPiBTcGVjaWFsIFZhbHVlczwvaDI+DQo8aDM+IEluZmluaXR5IDwvaDM+DQpPY2N1cnMgd2hlbiBhIG51bWJlciBpcyB0b28gbGFyZ2UgZm9yIFIgdG8gcmVwcmVzZW50Lg0KVGhpcyBpbmYgaXMgY2FzZSBzZW5zaXRpdmUuDQpJdCBjYW4gb25seSBiZSBhc3NvY2lhdGVkIHdpdGggbnVtZXJpYyB2YWx1ZXMNCg0KDQpgYGB7cn0NCmZvbzwtSW5mDQpmb28NCmJhcjwtYygzNDAxLEluZiwzLjEsLTU1NSxJbmYsNDMpDQpiYXINCg0KYGBgDQpgYGB7cn0NCmJhcjwtOTAwMDBeMTAwDQpiYXINCg0KYGBgDQoNCmBgYHtyfQ0KIyBleGFtcGxlIG9mIG5lZ2F0aXZlIEluZg0KcXV4IDwtYyg0MiwtSW5mLC1JbmYsSW5mLC00NTYyMy4zKQ0KcXV4DQoNCmBgYA0KDQoNCk5vdGUsIHlvdSBjYW4gY2FsY3VsYXRlIHVzaW5nIEluZiBmb3IgZXhhbXBsZQ0KYGBge3J9DQpJbmYqIC04DQoNCmBgYA0KWU91IHdpbGwgc2VlIHRoYXQgbXVsdGlwbHlpbmcgSW5mIHdpdGggYSBuZWdhdGl2ZSBudW1iZXIgcHJvZHVjZXMgYSBuZWdhdGl2ZSBJbmYuDQpJZiB5b3UgYWRkIG9yIG11bHRpcGx5IGluZmluaXR5IHlvdSBhbHNvIGdldCBpbmZpbml0eSBhcyBhIHJlc3VsdA0KYGBge3J9DQpJbmYrMQ0KNCotSW5mDQotNDUuMi1JbmYNCkluZi00NS4yDQpJbmYrSW5mDQpJbmYvMjMNCg0KDQpgYGANCg0KWmVybyBhbmQgaW5maW5pdHkgZ28gaGFuZCBpbiBoYW5kLg0KQW55IGZpbml0ZSBudW1iZXIgZGl2ZWQgYnkgaW5maW5pdHkgd2lsbCByZXN1bHQgaW4gemVyby4NCg0KYGBge3J9DQotNTkvSW5mDQo1OS9JbmYNCi01OS8tSW5mDQo1OS8tSW5mDQoNCmBgYA0KQW55IG51bWJlciBkaXZpZGVkIGJ5IHplcm8gYmVjb21lcyBJbmYNCmBgYHtyfQ0KNTYvMA0KLTU5LzANCg0KYGBgDQoNClRvIGRldGVjdCBJbmYgdmFsdWVzLCB1c2UgaXMuaW5maW5pdGUgb3IgaXMuZmluaXRlIA0KDQpgYGB7cn0NCiMgc2VlIHRoZSBjb250ZW50cyBvZiBxdXgNCnF1eA0KIyB0ZXN0IGZvciBpbmYgdmFsdWVzDQppcy5pbmZpbml0ZSh4PXF1eCkNCiMgdGVzdCBmb3IgZmluaXRlIHZhbHVlcw0KaXMuZmluaXRlKHg9cXV4KQ0KYGBgDQoNCk5vdGljZSB0aGF0IHRoZXNlIGZ1bmN0aW9ucyBkb2VzIG5vdCB0ZXN0IGZvciBlaXRoZXIgK0luZiBvciAtSW5mLg0KRmluYWxseSByZWxhdGlvbmFsIG9wZXJhdG9ycyB3b3JrIG9uIEluZiB2YWx1ZXMNCmBgYHtyfQ0KcXV4DQotSW5mPEluZg0KSW5mPi1JbmYNCnF1eD09SW5mDQpxdXg9PS1JbmYNCg0KYGBgDQoNCjxoMz4gTmFuIE5vdCBhIG51bWJlciA8L2gzPg0KVXNlZCB3aGVuIGl0IGlzIGltcG9zc2libGUgdG8gZXhwcmVzcyB0aGUgcmVzdWx0IG9mIGEgY2FsY3VsYXRpb24gdXNpbmcgYSBudW1iZXIsIEluZiwgb3IgLUluZg0KDQpgYGB7cn0NCmZvbzwtTmFODQpmb28NCmJhcjwtYyhOYU4sNTQuMywtMixOYU4sOTAwOTQuMTIzLC1JbmYsNTUpDQpiYXINCg0KYGBgDQpXaGVuZXZlciB5b3UgYXR0ZW1wdCB0byBjYW5jZWwgSW5mIGluIGFueSB3YXksIHRoZSByZXN1bHQgd2lsbCBiZSBOYW4NCg0KYGBge3J9DQotSW5mK0luZg0KSW5mL0luZg0KDQpgYGANCg0KTm90ZSB0aGF0IGFueSBtYXRoZWF0aWNhbCBvcGVyYXRpb24gaW52b2x2aW5nIE5hTiB3aWxsIHNpbXBseSByZXN1bHQgaW4gTmFuDQoNCmBgYHtyfQ0KTmFOKzENCjIrNiooNC00KS8wDQozLjVeKC1JbmYvSW5mKQ0KDQpgYGANCg0KRnVuY3Rpb25zIHRvIGRldGVjdCBOYW4gOiBJcy5OYU4NCmBgYHtyfQ0KYmFyDQppcy5uYW4oeD1iYXIpDQohaXMubmFuKHg9YmFyKQ0KaXMubmFuKHg9YmFyKXwgaXMuaW5maW5pdGUoeD1iYXIpDQpiYXJbLXdoaWNoKGlzLm5hbih4PWJhcil8aXMuaW5maW5pdGUoeD1iYXIpKV0NCmBgYA0KDQpOT3RlOiANClRoZSBsYXN0IGxpbmUgdXNlcyBXSElDSCBjb21tYW5kIHRvIGNvbnZlcnQgdGhlc2UgbG9naWNhbCB2YWx1ZXMgaW50byBudW1iZXJjIGluZGV4IHBvc2l0aW9ucyBzbyB0aGF0IHlvdSBjYW4gcmVtb3ZlIHRoZW0gd2l0aCB0aGUgbmVnYXRpdmUgaW5kZXhlcyBpbiB0aGUgc3F1YXJlIGJyYWNrZXRzLg0KDQo8aDM+TmEgTm90IEF2YWlsYWJsZSA8L2gzPg0KTkEgaXMgdXNlZCBmb3IgbWlzc2luZyB2YWx1ZXMgaW4gZGF0YSBzZXRzLg0KV2hlcmVhcyBOYU4gaXMgb25seSB1c2VkIGZvciBudW1lcmljIHZhbHVlcywgTkEgY2FuIGFsc28gYmUgdXNlZCBmb3IgYm90aCBudW1lcmljIGFuZCBub24tbnVtYmVyaWMgc2V0dGluZ3MuDQoNCmBgYHtyfQ0KZm9vPC1jKCJjaGFyYWN0ZXIiLCJhIixOQSwid2l0aCIsInN0cmluZyIsTkEpDQpmb28NCg0KYGBgDQpgYGB7cn0NCmJhcjwtZmFjdG9yKGMoImJsdWUiLE5BLE5BLCJibHVlIiwiZ3JlZW4iLCJibHVlIixOQSwicmVkIiwicmVkIixOQSwiZ3JlZW4iKSkNCmJhcg0KYGBgDQpgYGB7cn0NCmJheiA8LW1hdHJpeChjKDE6MyxOQSw1LDYsTkEsOCxOQSksbnJvdz0zLG5jb2w9MykNCmJheg0KYGBgDQpVc2UgSXMubmEgdG8gZGV0ZWN0IE5BIGVsZW1lbnRzDQpgYGB7cn0NCmJheg0KaXMubmEoeD1iYXopDQpgYGANCmBgYHtyfQ0KIyBpcy5uYSBhbHNvIGZsYWdzIHRoZSBOYU4gdmFsdWVzDQpxdXg8LWMoTkEsNS44OSxJbmYsTkEsOS40MywtMi4zNSxOYU4sMi4xMCwtOC41MywtNy41OCxOQSwtNC41OCwyLjAxLE5hTikNCnF1eA0KaXMubmEoeD1xdXgpDQpgYGANCklmIHlvdSB3YW50IHRvIGlkZW50aWZ5IE5hIGFuZCBOYU4gc2VwYXJhdGVseQ0KYGBge3J9DQpxdXgNCndoaWNoKHg9aXMubmFuKHg9cXV4KSkNCmBgYA0KSWYgeW91IHdhbnQgdG8gaWRlbnRpZnkgTmFOIHZhbHVlcyBzcGVjaWZpY2FsbHk6DQoNCmBgYHtyfQ0KcXV4DQp3aGljaCh4PShpcy5uYSh4PXF1eCkmIWlzLm5hbih4PXF1eCkpKQ0KDQpgYGANCkFmdGVyIGxvY2F0aW5nIHRoZSBvZmZlbmRpbmcgZWxlbWVudHMsIHlvdSBhbiB1c2UgbmVnYXRpdmUgaW5kZXhlcyBpbiBzcXVhcmUgYnJhY2tldHMgdG8gcmVtb3ZlIHRoZW0uDQpBbm90aGVyIHdheSBpbiBSIGlzIHRvIHVzZSB0aGUgbmEub21pdC4gSXQgd2lsbCB0YWtlIGEgc3RydWN0dXJlIGFuZCBkZWxldGUgYWxsIE5BcyBmcm9tIGl0LiANCm5hLm9taXQgd2lsbCBhbHNvIGFwcGx5IHRvIE5hTnMgaWYgdGhlIGVsZW1lbnRzIGFyZSBudW1lcmljDQoNCmBgYHtyfQ0KcXV4DQpxdXV4PC1uYS5vbWl0KG9iamVjdD1xdXgpDQpxdXV4DQoNCmBgYA0KDQoNCjxoMz4gTnVsbCA8L2gzPg0KVXNlZCB0byBFWFBMSUNJVExZIGRlZmluZSBhbiBlbXB0eSBlbnRpdHkuDQpOb3RpY2UgdGhlcmUgaXMgTk8gaW5kZXggcG9zaXRpb24gZm9yIE5VTEwgdmFsdWVzDQoNCmBgYHtyfQ0KZm9vIDwtIE5VTEwNCmZvbw0KYmFyIDwtIE5BDQpiYXINCg0KYGBgDQoNCmBgYHtyfQ0KYygyLDQsTkEsOCkNCmMoMiw0LE5VTEwsOCkNCmBgYA0KVGhlIGxpbmUgdXNpbmcgTkEgaGFzIDQgZWxlbWVudHMsIHdoaWxlIHRoZSBsaW5lIHVzaW5nIE5VTEwgaGFzIG9ubHkgMyBlbGVtZW50cw0KTnVsbCBjYW5ub3QgdGFrZSB1cCBhIHBvc2l0aW9uIGluIHRoZSB2ZWN0b3IuDQoNClRoZSBpcy5udWxsIGZ1bmN0aW9uIGlzIHVzZWQgdG8gY2hlY2sgd2hlbiBhbiBlbGVtZW50IGlzIGV4cGxpY2l0bHkgTlVMTC4NCmBgYHtyfQ0Kb3B0LmFyZyA8LWMoInN0cmluZzEiLCJzdHJpbmcyIiwic3RyaW5nMyIpDQpvcHQuYXJnDQppcy5uYSh4PW9wdC5hcmcpDQppcy5udWxsKHg9b3B0LmFyZykNCg0KYGBgDQpJZiB0aGUgYXJndW1lbnQgaXMgZW1wdHkgdXNpbmcgTlVMTCBvdmVyIE5BIGZvciB0aGUgY2hlY2sgaXMgYmV0dGVyLg0KYGBge3J9DQpvcHQuYXJnPC1jKE5BLE5BLE5BKQ0KaXMubmEoeD1vcHQuYXJnKQ0KDQpgYGANCmBgYHtyfQ0Kb3B0LmFyZzwtYyhOVUxMLE5VTEwsTlVMTCkNCmlzLm51bGwoeD1vcHQuYXJnKQ0KDQpvcHQuYXJnPC1jKE5VTEwsTlVMTCwxKQ0KaXMubnVsbCh4PW9wdC5hcmcpDQpvcHQuYXJnPC1jKE5VTEwsTlVMTCxOQSkNCmlzLm51bGwoeD1vcHQuYXJnKQ0KYGBgDQoNCk51bGwgZG9taW5hdGVzIGFueSBhcml0aG1ldGljLCBldmVuIGlmIGl0IGluY2x1ZGVzIG90aGVyIHNwZWNpYWwgdmFsdWVzDQoNCmBgYHtyfQ0KTlVMTCs1Mw0KNTM8TlVMTA0KDQpOYU4tTlVMTCtOQS9JbmYNCg0KDQpgYGANCg0KDQo8aDI+VW5kZXJzdGFuZGluZyBUeXBlcywgQ2xhc3NlcyBhbmQgQ29lcmNpb24gPC9oMj4NCjxoMz4gQXR0cmlidXRlczwvaDM+DQoNCkF0dHJpYnV0ZXMgY2FuIGJlIGltcGxpY2l0IG9yIGV4cGxpY2l0Lg0KYGBge3J9DQpmb288LW1hdHJpeChkYXRhPTE6OSxucm93PTMsbmNvbD0zKQ0KZm9vDQphdHRyaWJ1dGVzKGZvbykNCmBgYA0KWW91IGNhbiBhbHNvIHVzZSB0aGlzOg0KYGBge3J9DQphdHRyKHg9Zm9vLHdoaWNoPSJkaW0iKQ0KDQpgYGANCg0KV2hlbiBhZGRpdGlvbmFsIGF0dHJpYnV0ZXMgYXJlIGNyZWF0ZWQsIHlvdSBjYW4gc2VlIGFsbCB3aXRoIHRoZSBhdHRyaWJ1dGVzIGNvbW1hbmQNCkluIHRoaXMgZXhhbXBsZSwgd2UgYWRkZWQgZGltbmFtZXMgdG8gdGhlIG1hdHJpeC4NCg0KDQpgYGB7cn0NCmZvbzwtbWF0cml4KGRhdGE9MTo5LG5yb3c9MyxuY29sPTMsZGltbmFtZXM9bGlzdChjKCJhIiwiYiIsImMiKSxjKCJkIiwiZSIsImYiKSkpDQpmb28NCmF0dHJpYnV0ZXMoZm9vKQ0KDQpgYGANCg0KV2UgY2FuIGV4dHJhY3QgdGhlIHZhbHVlcyBvZiBkaW1uYW1lcyBhcyB3ZWxsDQoNCmBgYHtyfQ0KZGltbmFtZXMoZm9vKQ0KYGBgDQp3ZSBjYW4gYWxzbyBhc3NpZ24gYW4gb2JqZWN0IGFmdGVyIGl0IGhhcyBiZWVuIGNyZWF0ZWQuDQoNCmBgYHtyfQ0KZm9vDQpiYXI8LWxpc3QoYygiYSIsImIiLCJjIiksYygiZCIsImUiLCJmIikpDQpiYXINCg0KYGBgDQoNCjxoMz5PYmplY3QgQ2xhc3M8L2gzPg0KRXZlcnkgb2JqZWN0IHdlIGNyZWF0ZSBpcyBpZGVudGlmaWVkIGVpdGhlciBpbXBsaWNpdGx5IG9yIGV4cGxpY2l0bHkgd2hpdGggYXQgbGVhc3Qgb25lIGNsYXNzLg0KDQpgYGB7cn0NCm51bS52ZWMxPC0xOjQNCm51bS52ZWMxDQpudW0udmVjMjwtc2VxKGZyb209MSwgdG89NCxsZW5ndGg9NikNCm51bS52ZWMyDQpjaGFyLnZlYzwtYygiYSIsImZldyIsInN0cmluZ3MiLCJoZXJlIikNCmNoYXIudmVjDQpsb2dpYy52ZWM8LWMoVCxGLEYsRixULEYsVCxUKQ0KZmFjLnZlYzwtZmFjdG9yKGMoIkJsdWUiLCJCbHVlIiwiR3JlZW4iLCJSZWQiLCJHcmVlbiIsIlllbGxvdyIpKQ0KZmFjLnZlYw0KDQpgYGANCg0KDQpTdGFuZGFsb25lIHZlY3RvcnMNCmBgYHtyfQ0KY2xhc3MobnVtLnZlYzEpDQpjbGFzcyhudW0udmVjMikNCmNsYXNzKGNoYXIudmVjKQ0KY2xhc3MobG9naWMudmVjKQ0KY2xhc3MoZmFjLnZlYykNCmBgYA0KT3RoZXIgRGF0YSBTdHJ1Y3R1cmVzDQpgYGB7cn0NCm51bS52ZWMxDQpudW0ubWF0MTwtbWF0cml4KGRhdGE9bnVtLnZlYzEsbnJvdz0yLG5jb2w9MikNCm51bS5tYXQxDQpjbGFzcyhudW0ubWF0MSkNCg0KbnVtLnZlYzINCm51bS5tYXQyPC1tYXRyaXgoZGF0YT1udW0udmVjMixucm93PTIsbmNvbD0zKQ0KbnVtLm1hdDINCmNsYXNzKG51bS5tYXQyKQ0KDQpjaGFyLnZlYw0KY2hhci5tYXQ8LW1hdHJpeChkYXRhPWNoYXIudmVjLG5yb3c9MixuY29sPTIpDQpjaGFyLm1hdA0KY2xhc3MoY2hhci5tYXQpDQoNCmxvZ2ljLnZlYw0KbG9naWMubWF0PC1tYXRyaXgoZGF0YT1sb2dpYy52ZWMsbnJvdz00LG5jb2w9MikNCmxvZ2ljLm1hdA0KY2xhc3MobG9naWMubWF0KQ0KDQpgYGANCk11bHRpcGxlIENsYXNzZXMNCkNlcnRhaW4gb2JqZWN0cyB3aWxsIGhhdmUgbXVsdGlwbGUgY2xhc3Nlcy4NCkEgdmFyaWFudCBvbiBhIHN0YW5kYXJkIGZvcm0gb2YgYW4gb2JqZWN0IHN1Y2ggYW4gb3JkZXJlZCBmYWN0b3IgdmVjdG9yLCB3aWxsIGluaGVyaXQgdGhlIHVzdWFsIGZhY3RvciBjbGFzcyBhbmQgYWxzbyBjb250YWluIHRoZSBhZGRpdGlvbmFsIG9yZGVyZWQgY2xhc3MuDQpCb3RoIGFyZSByZXR1cm5lZCBpZiB5b3UgdXNlIHRoZSBjbGFzcyBmdW5jdGlvbg0KDQpgYGB7cn0NCm9yZGZhYy52ZWM8LWZhY3Rvcih4PWMoIlNtYWxsIiwiTGFyZ2UiLCJMYXJnZSIsIlJlZ3VsYXIiLCJTbWFsbCIpLGxldmVscz1jKCJTbWFsbCIsIlJlZ3VsYXIiLCJMYXJnZSIpLG9yZGVyZWQ9VFJVRSkNCm9yZGZhYy52ZWMNCg0KY2xhc3Mob3JkZmFjLnZlYykNCg0KYGBgDQo8aDM+SXMtRG90IENoZWNraW5nIEZ1bmN0aW9ucyA8L2gzPg0KVG8gY2hlY2sgd2hldGhlciB0aGUgb2JqZWN0IGlzIGEgc3BlY2lmaWMgY2xhc3Mgb3IgZGF0YSB0eXBlLCB5b3UgY2FuIHVzZSB0aGUgImlzLWRvdCBmdW5jdGlvbiIgb24gdGhlIG9iamVjdCBhbmQgaXQgd2lsbCByZXR1cm4gVFJVRSBvciBGYWxzZSBsb2dpY2FsIHZhbHVlLg0KDQpgYGB7cn0NCm51bS52ZWMxIDwtMTo0DQpudW0udmVjMQ0KaXMuaW50ZWdlcihudW0udmVjMSkNCmlzLm51bWVyaWMobnVtLnZlYzEpDQppcy5tYXRyaXgobnVtLnZlYzEpDQppcy5kYXRhLmZyYW1lKG51bS52ZWMxKQ0KaXMudmVjdG9yKG51bS52ZWMxKQ0KaXMubG9naWNhbChudW0udmVjMSkNCg0KYGBgDQo8aDM+QVMtRG90IENvZXJjaW9uIEZ1bmN0aW9ucyA8L2gzPg0KDQpDb2VyY2lvbiBpcyB0aGUgY29udmVyc2lvbiBvZiBvbmUgZGF0YSB0eXBlIHRvIGFub3RoZXIuDQpJbXBsaWNpdCBjb2VyY2lvbiBvY2N1cnMgbmF0dXJhbGx5IHdoZW4gZWxlbWVudHMgbmVlZCB0byBiZSBjb252ZXJ0ZWQgdG8gYW5vdGhlciB0eXBlIGluIG9yZGVyIGZvciBhbiBvcGVyYXRpb24gdG8gY29tcGxldGUuDQpUcnVlIG9yIGZhbHNlIGFyZSBpbXBsaWNpdGx5IGNvZXJjZWQgaW50byAxIGZvciB0cnVlIGFuZCAwIGZvciBmYWxzZS4NCg0KYGBge3J9DQoxOjQrYyhULEYsRixUKQ0KYGBgDQoNCkFub3RoZXIgZXhhbXBsZSBvZiBjb2VyY2lvbiB3aGVuIHlvdSBwYXN0ZSBhbmQgY2F0IHRvIGdsdWUgdG9nZXRoZXIgY2hhcmFjdGVyIHN0cmluZ3MuIE5vbi1jaGFyYWN0ZXIgc3RyaW5ncyBhcmUgYXV0b21hdGljYWxseSBjb2VyY2VkIGludG8gc3RyaW5ncyBiZWZvcmUgdGhlIGNvbmNhdGVuYXRpb24gdGFrZXMgcGxhY2VzLiANCg0KYGBge3J9DQpmb28gPC0zNA0KYmFyPC1UDQpwYXN0ZSgiRGVmaW5pdGVseSBmb286IiwgZm9vLCAiOyBkZWZpbml0ZWx5IGJhcjogIixiYXIsIi4iLHNlcD0iICIpDQpgYGANCg0KDQoNCg0KDQogIA==