Objek: Class System S4
- Class System S4: Mengatasi masalah dalam Class System S3 dengan sistem objek lebih formal
- Salah satu keuntungan: sistem penurunan dari class/objek
- Dalam sistem objek formal: setiap objek didefinisikan secara formal dalam suatu class
- Sebuah class terdiri dari slot dengan tipe atau class spesifik
- Class dideklarasikan dengan fungsi setClass berikut
setClass("coords",
representation(x = "numeric", y = "numeric"))
Konstruktor dan Aksesor
Berikut adalah definisi ulang class sebelumnya ke Class System S4 dengan menggunakan fungsi konstruktor:
coords <- function(x, y){
if (length(x) != length(y))
stop("length x dan y harus bernilai sama")
if (!is.numeric(x) || !is.numeric(y))
stop("x dan y harus vektor numeric")
new("coords", x = as.vector(x), y = as.vector(y))
}
pts <- coords(round(rnorm(5), 2), round(rnorm(5),2))
pts
An object of class "coords"
Slot "x":
[1] 0.69 0.20 1.72 1.76 -2.62
Slot "y":
[1] 0.77 -1.34 -0.57 0.52 0.29
Akses terhadap slot menggunakan fungsi slot atau operator @
#Menggunakan fungsi slot
slot(pts,"x")
[1] 0.69 0.20 1.72 1.76 -2.62
[1] 0.77 -1.34 -0.57 0.52 0.29
#Menggunakan operayor @
pts@x
[1] 0.69 0.20 1.72 1.76 -2.62
[1] 0.77 -1.34 -0.57 0.52 0.29
Penggunaan fungsi slot dan operator @ menghasilkan output yang sama. Tetapi lebih disarankan mengakses dengan 2 fungsi seperti sebelumnya.
xcoords <- function(obj) obj@x
ycoords <- function(obj) obj@y
xcoords(pts)
[1] 0.69 0.20 1.72 1.76 -2.62
[1] 0.77 -1.34 -0.57 0.52 0.29
Fungsi Generik
Fungsi generik diciptakan menggunakan fungsi setMethod . Argumen didefinisikan dalam signature seperti berikut.
setMethod(show, signature(object = "coords"),
function(object)
print(paste("(",
format(xcoords(object)),", ",
format(ycoords(object)), ")", sep=""),
quote=FALSE))
pts
[1] ( 0.69, 0.77) ( 0.20, -1.34)
[3] ( 1.72, -0.57) ( 1.76, 0.52)
[5] (-2.62, 0.29)
Fungsi generik show setara dengan fungsi print pada class system S3.
Definisi Fungsi Generik Baru
Mendefinisikan fungsi baru sebagai fungsi generik menggunakan:setGeneric dan setMethod Sebagai contoh akan dibuat fungsi generik berikut.
Fungsi Generik Bbox
setGeneric("bbox", function(obj)
standardGeneric("bbox"))
[1] "bbox"
setMethod("bbox", signature(obj = "coords"),
function(obj)
matrix(c(range(xcoords(obj)),
range(ycoords(obj))),
nc = 2,
dimnames = list(
c("min", "max"),
c("x:", "y:"))))
bbox(pts)
x: y:
min -2.62 -1.34
max 1.76 0.77
[1] ( 0.69, 0.77) ( 0.20, -1.34)
[3] ( 1.72, -0.57) ( 1.76, 0.52)
[5] (-2.62, 0.29)
Fungsi Generik Plot
setMethod("plot", signature(x="coords"),
function(x, bbox=FALSE, ...)
{if (bbox) {
plot(xcoords(x),ycoords(x), ...);
x.1 <- c(bbox(x)[1],bbox(x)[2],bbox(x)[2],bbox(x)[1]);
y.1 <- c(bbox(x)[3],bbox(x)[3],bbox(x)[4],bbox(x)[4]);
polygon(x.1,y.1)}
else {plot(xcoords(x),ycoords(x), ...)}})
plot(pts)

plot(pts,bbox=T, pch=19, col="blue",xlab="x", ylab="y")

Pewarisan Class
Terdapat class baru yang diturunkan dari coords dengan menambahkan slot nilai
setClass("vcoords", representation(nilai = "numeric"), contains = "coords")
vcoords <- function(x, y, nilai){
if ((length(x) != length(y)) || (length(x) != length(nilai)))
stop("length x, y, dan nilai harus bernilai sama")
if (!is.numeric(x) || !is.numeric(y) || !is.numeric(nilai))
stop("x, y, dan nilai harus vektor numeric")
new("vcoords", x = as.vector(x), y = as.vector(y),
nilai = as.vector(nilai))
}
nilai <- function(obj) obj@nilai
vpts <- vcoords(xcoords(pts), ycoords(pts), round(100*runif(5)))
vpts
[1] ( 0.69, 0.77) ( 0.20, -1.34)
[3] ( 1.72, -0.57) ( 1.76, 0.52)
[5] (-2.62, 0.29)
[1] ( 0.69, 0.77) ( 0.20, -1.34)
[3] ( 1.72, -0.57) ( 1.76, 0.52)
[5] (-2.62, 0.29)
Karena ada penambahan slot nilai sehingga method yang diwariskan perlu didefinisi ulang seperti berikut.
Method Show
setMethod(show, signature(object = "vcoords"),
function(object)
print(paste("(",
format(xcoords(object)), ", ",
format(ycoords(object)), "; ",
format(nilai(object)),")",
sep=""),
quote=FALSE))
vpts
[1] ( 0.69, 0.77; 75)
[2] ( 0.20, -1.34; 31)
[3] ( 1.72, -0.57; 47)
[4] ( 1.76, 0.52; 38)
[5] (-2.62, 0.29; 54)
[1] ( 0.69, 0.77) ( 0.20, -1.34)
[3] ( 1.72, -0.57) ( 1.76, 0.52)
[5] (-2.62, 0.29)
Method Plot
setMethod("plot", signature(x="vcoords"),
function(x, txt=FALSE, bbox=FALSE, ...){
if (bbox) {
if (!txt) {plot(xcoords(x),ycoords(x), ...);}
else {
plot(xcoords(x),ycoords(x), type="n", ...);
text(xcoords(x),ycoords(x), nilai(x), ...)}
x.1 <- c(bbox(x)[1],bbox(x)[2],bbox(x)[2],bbox(x)[1]);
y.1 <- c(bbox(x)[3],bbox(x)[3],bbox(x)[4],bbox(x)[4]);
polygon(x.1,y.1)}
else {
if (!txt) {plot(xcoords(x),ycoords(x), ...)}
else {
plot(xcoords(x),ycoords(x), type="n", ...);
text(xcoords(x),ycoords(x), nilai(x), ...);}}
})
plot(vpts)

plot(vpts, txt=T, bbox=T, pch=19, col="blue")

Transformasi dengan Matematik
Sebagai contoh dilakukan tranformasi terhadap nilai dengan cos berikut.
setMethod("cos", signature(x = "vcoords"),
function(x)
vcoords(xcoords(x),
ycoords(x),
cos(nilai(x))))
cos(vpts)
[1] ( 0.69, 0.77; 0.9217513)
[2] ( 0.20, -1.34; 0.9147424)
[3] ( 1.72, -0.57; -0.9923355)
[4] ( 1.76, 0.52; 0.9550736)
[5] (-2.62, 0.29; -0.8293098)
Seperti pada class system S3, terdapat group Math seperti berikut.
setMethod("Math", signature(x = "vcoords"),
function(x) vcoords(xcoords(x), ycoords(x), callGeneric(nilai(x))))
cos(vpts)
[1] ( 0.69, 0.77; 0.9217513)
[2] ( 0.20, -1.34; 0.9147424)
[3] ( 1.72, -0.57; -0.9923355)
[4] ( 1.76, 0.52; 0.9550736)
[5] (-2.62, 0.29; -0.8293098)
[1] ( 0.69, 0.77; -0.3877816)
[2] ( 0.20, -1.34; -0.4040376)
[3] ( 1.72, -0.57; 0.1235731)
[4] ( 1.76, 0.52; 0.2963686)
[5] (-2.62, 0.29; -0.5587890)
[1] ( 0.69, 0.77; 8.660254)
[2] ( 0.20, -1.34; 5.567764)
[3] ( 1.72, -0.57; 6.855655)
[4] ( 1.76, 0.52; 6.164414)
[5] (-2.62, 0.29; 7.348469)
Operasi Aritmetika
Langkah dalam menyusun operasi aritmetika mirip dengan sistem S3 yaitu menyusun fungsi untuk lokasi terlebih dahulu.
sameloc <- function(e1, e2)
(length(nilai(e1)) == length(nilai(e2)) || any(xcoords(e1) == xcoords(e2))
|| any(ycoords(e1) == ycoords(e2)))
lalu menyusun method aritmetika dengan kedua operand merupakan kelas vcoords, seperti berikut.
setMethod("Arith", signature(e1 = "vcoords", e2 = "vcoords"),
function(e1, e2){
if (!sameloc(e1, e2))
stop("Dibutuhkan titik identik")
vcoords(xcoords(e1), ycoords(e2),
callGeneric(nilai(e1), nilai(e2)))
})
vpts
[1] ( 0.69, 0.77; 75)
[2] ( 0.20, -1.34; 31)
[3] ( 1.72, -0.57; 47)
[4] ( 1.76, 0.52; 38)
[5] (-2.62, 0.29; 54)
[1] ( 0.69, 0.77; 150)
[2] ( 0.20, -1.34; 62)
[3] ( 1.72, -0.57; 94)
[4] ( 1.76, 0.52; 76)
[5] (-2.62, 0.29; 108)
Error in 2 + vpts : non-numeric argument to binary operator
bisa dilihat pada saat mengoperasikan suatu numerik sebagai operand, operasi mengalami error. Maka perlu menambahkan setMethod("Arith",...) untuk operasi dengan numerik seperti berikut. ## Menambah operasi aritmetika dengan awal numerik
setMethod("Arith", signature(e1 = "numeric", e2 = "vcoords"),
function(e1, e2){
if (length(e1)>length(nilai(e2)))
stop("length yang tidak benar")
vcoords(xcoords(e2), ycoords(e2),
callGeneric(as.vector(e1), nilai(e2)))
})
vpts
[1] ( 0.69, 0.77; 75)
[2] ( 0.20, -1.34; 31)
[3] ( 1.72, -0.57; 47)
[4] ( 1.76, 0.52; 38)
[5] (-2.62, 0.29; 54)
[1] ( 0.69, 0.77; 77)
[2] ( 0.20, -1.34; 33)
[3] ( 1.72, -0.57; 49)
[4] ( 1.76, 0.52; 40)
[5] (-2.62, 0.29; 56)
[1] ( 0.69, 0.77; 150)
[2] ( 0.20, -1.34; 62)
[3] ( 1.72, -0.57; 94)
[4] ( 1.76, 0.52; 76)
[5] (-2.62, 0.29; 108)
Menambah operasi aritmetika dengan akhir numerik
setMethod("Arith", signature(e1 = "vcoords", e2 = "numeric"),
function(e1, e2){
if (length(nilai(e1)) < length(e2))
stop("length yang tidak benar")
vcoords(xcoords(e1), ycoords(e1),
callGeneric(nilai(e1), as.vector(e2)))
})
vpts+2
[1] ( 0.69, 0.77; 77)
[2] ( 0.20, -1.34; 33)
[3] ( 1.72, -0.57; 49)
[4] ( 1.76, 0.52; 40)
[5] (-2.62, 0.29; 56)
Method Subset
Mendefinisikan method untuk subset dengan syntax berikut.
setMethod("[", signature(x = "vcoords", i = "ANY",
j = "missing", drop = "missing"),
function(x, i, j) vcoords(xcoords(x)[i], ycoords(x)[i],
nilai(x)[i]))
vpts[1:3]
[1] (0.69, 0.77; 75)
[2] (0.20, -1.34; 31)
[3] (1.72, -0.57; 47)
Pemeriksaan Suatu Class Objek
Untuk mengecek apakah suatu objek merupakan suatu class digunakan fungsi is() berikut.
[1] TRUE
[1] FALSE
[1] TRUE
[1] TRUE
Untuk men-coerce objek ke objek lain dari suatu class digunakan fungsi as() berikut.
[1] ( 0.69, 0.77) ( 0.20, -1.34)
[3] ( 1.72, -0.57) ( 1.76, 0.52)
[5] (-2.62, 0.29)
output di atas menampilkan vpts sebagai coords, sehingga hanya menampilkan xcoords dan ycoords tanpa nilai.
[1] ( 0.69, 0.77; ) ( 0.20, -1.34; )
[3] ( 1.72, -0.57; ) ( 1.76, 0.52; )
[5] (-2.62, 0.29; )
output di atas menampilkan pts sebagai vcoords, sehingga menampilkan xcoords, ycoords, dan nilai. Namun karena pts tidak memiliki atribut nilai maka ditampilkan sebagai blankspace setelah tanda ;
LS0tDQp0aXRsZTogIlBlbXJvZ3JhbWFuIEJlcm9yaWVudGFzaSBPYmplayBkYWxhbSBSIENsYXNzIFN5c3RlbSBTNCINCmF1dGhvcjogIkFubmlzc2EgTnVyIEZpdHJpYSBGYXRoaW5hXltNYWhhc2lzd2EgU3RhdGlzdGlrYSBkYW4gU2FpbnMgRGF0YSBJUEIsIGFubmlzc2FfbmZmQGFwcHMuaXBiLmFjLmlkXSINCmRhdGU6ICI0LzQvMjAyMiINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0aGVtZTogZGFya2x5DQogICAgaGlnaGxpZ2h0OiBtb25vY2hyb21lDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiBzZW50ZW5jZQ0KLS0tDQoNCiMgT2JqZWs6IENsYXNzIFN5c3RlbSBTNA0KDQotICAgQ2xhc3MgU3lzdGVtIFM0OiBNZW5nYXRhc2kgbWFzYWxhaCBkYWxhbSBDbGFzcyBTeXN0ZW0gUzMgZGVuZ2FuIHNpc3RlbSBvYmplayBsZWJpaCBmb3JtYWwNCi0gICBTYWxhaCBzYXR1IGtldW50dW5nYW46IHNpc3RlbSBwZW51cnVuYW4gZGFyaSBjbGFzcy9vYmplaw0KLSAgIERhbGFtIHNpc3RlbSBvYmplayBmb3JtYWw6IHNldGlhcCBvYmplayBkaWRlZmluaXNpa2FuIHNlY2FyYSBmb3JtYWwgZGFsYW0gc3VhdHUgY2xhc3MNCi0gICBTZWJ1YWggY2xhc3MgdGVyZGlyaSBkYXJpIHNsb3QgZGVuZ2FuIHRpcGUgYXRhdSBjbGFzcyBzcGVzaWZpaw0KLSAgIENsYXNzIGRpZGVrbGFyYXNpa2FuIGRlbmdhbiBmdW5nc2kgc2V0Q2xhc3MgYmVyaWt1dA0KDQpgYGB7cn0NCnNldENsYXNzKCJjb29yZHMiLCANCiAgICAgICAgIHJlcHJlc2VudGF0aW9uKHggPSAibnVtZXJpYyIsIHkgPSAibnVtZXJpYyIpKQ0KDQpgYGANCg0KIyBLb25zdHJ1a3RvciBkYW4gQWtzZXNvcg0KQmVyaWt1dCBhZGFsYWggZGVmaW5pc2kgdWxhbmcgY2xhc3Mgc2ViZWx1bW55YSBrZSBDbGFzcyBTeXN0ZW0gUzQgZGVuZ2FuIG1lbmdndW5ha2FuIGZ1bmdzaSBrb25zdHJ1a3RvcjoNCmBgYHtyfQ0KY29vcmRzIDwtIGZ1bmN0aW9uKHgsIHkpew0KICBpZiAobGVuZ3RoKHgpICE9IGxlbmd0aCh5KSkNCiAgICBzdG9wKCJsZW5ndGggeCBkYW4geSBoYXJ1cyBiZXJuaWxhaSBzYW1hIikNCiAgaWYgKCFpcy5udW1lcmljKHgpIHx8ICFpcy5udW1lcmljKHkpKQ0KICAgIHN0b3AoInggZGFuIHkgaGFydXMgdmVrdG9yIG51bWVyaWMiKQ0KICBuZXcoImNvb3JkcyIsIHggPSBhcy52ZWN0b3IoeCksIHkgPSBhcy52ZWN0b3IoeSkpDQogIH0NCnB0cyA8LSBjb29yZHMocm91bmQocm5vcm0oNSksIDIpLCByb3VuZChybm9ybSg1KSwyKSkNCnB0cw0KDQpgYGANCg0KQWtzZXMgdGVyaGFkYXAgc2xvdCBtZW5nZ3VuYWthbiBmdW5nc2kgc2xvdCBhdGF1IG9wZXJhdG9yIEAgDQpgYGB7cn0NCiNNZW5nZ3VuYWthbiBmdW5nc2kgc2xvdA0Kc2xvdChwdHMsIngiKSANCnNsb3QocHRzLCJ5IikNCiNNZW5nZ3VuYWthbiBvcGVyYXlvciBADQpwdHNAeA0KcHRzQHkNCmBgYA0KDQpQZW5nZ3VuYWFuIGZ1bmdzaSBzbG90IGRhbiBvcGVyYXRvciBAIG1lbmdoYXNpbGthbiBvdXRwdXQgeWFuZyBzYW1hLiBUZXRhcGkgbGViaWggZGlzYXJhbmthbiBtZW5nYWtzZXMgZGVuZ2FuIDIgZnVuZ3NpIHNlcGVydGkgc2ViZWx1bW55YS4NCmBgYHtyfQ0KeGNvb3JkcyA8LSBmdW5jdGlvbihvYmopIG9iakB4DQp5Y29vcmRzIDwtIGZ1bmN0aW9uKG9iaikgb2JqQHkNCnhjb29yZHMocHRzKQ0KeWNvb3JkcyhwdHMpDQpgYGANCg0KIyBGdW5nc2kgR2VuZXJpaw0KRnVuZ3NpIGdlbmVyaWsgZGljaXB0YWthbiBtZW5nZ3VuYWthbiBmdW5nc2kgYHNldE1ldGhvZGAgLiBBcmd1bWVuIGRpZGVmaW5pc2lrYW4gZGFsYW0gc2lnbmF0dXJlIHNlcGVydGkgYmVyaWt1dC4NCmBgYHtyfQ0Kc2V0TWV0aG9kKHNob3csIHNpZ25hdHVyZShvYmplY3QgPSAiY29vcmRzIiksDQogICAgICAgICAgZnVuY3Rpb24ob2JqZWN0KQ0KICAgICAgICAgICAgcHJpbnQocGFzdGUoIigiLA0KICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0KHhjb29yZHMob2JqZWN0KSksIiwgIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCh5Y29vcmRzKG9iamVjdCkpLCAiKSIsIHNlcD0iIiksDQogICAgICAgICAgICAgICAgICBxdW90ZT1GQUxTRSkpDQpwdHMNCmBgYA0KDQpGdW5nc2kgZ2VuZXJpayBgc2hvd2Agc2V0YXJhIGRlbmdhbiBmdW5nc2kgYHByaW50YCBwYWRhIGNsYXNzIHN5c3RlbSBTMy4NCg0KIyMgRGVmaW5pc2kgRnVuZ3NpIEdlbmVyaWsgQmFydQ0KTWVuZGVmaW5pc2lrYW4gZnVuZ3NpIGJhcnUgc2ViYWdhaSBmdW5nc2kgZ2VuZXJpayBtZW5nZ3VuYWthbjpgc2V0R2VuZXJpY2AgZGFuIGBzZXRNZXRob2RgIA0KU2ViYWdhaSBjb250b2ggYWthbiBkaWJ1YXQgZnVuZ3NpIGdlbmVyaWsgYmVyaWt1dC4NCg0KIyMjIEZ1bmdzaSBHZW5lcmlrIEJib3gNCmBgYHtyfQ0Kc2V0R2VuZXJpYygiYmJveCIsIGZ1bmN0aW9uKG9iaikNCiAgc3RhbmRhcmRHZW5lcmljKCJiYm94IikpDQoNCnNldE1ldGhvZCgiYmJveCIsIHNpZ25hdHVyZShvYmogPSAiY29vcmRzIiksDQogICAgICAgICAgZnVuY3Rpb24ob2JqKSANCiAgICAgICAgICAgIG1hdHJpeChjKHJhbmdlKHhjb29yZHMob2JqKSksDQogICAgICAgICAgICAgICAgICAgICByYW5nZSh5Y29vcmRzKG9iaikpKSwgDQogICAgICAgICAgICAgICAgICAgbmMgPSAyLCANCiAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QoDQogICAgICAgICAgICAgICAgICAgICBjKCJtaW4iLCAibWF4IiksDQogICAgICAgICAgICAgICAgICAgICBjKCJ4OiIsICJ5OiIpKSkpDQpiYm94KHB0cykgDQpwdHMNCmBgYA0KDQojIyMgRnVuZ3NpIEdlbmVyaWsgUGxvdA0KYGBge3J9DQpzZXRNZXRob2QoInBsb3QiLCBzaWduYXR1cmUoeD0iY29vcmRzIiksDQogICAgICAgICAgZnVuY3Rpb24oeCwgYmJveD1GQUxTRSwgLi4uKQ0KICAgICAgICAgICAge2lmIChiYm94KSB7DQogICAgICAgICAgICAgIHBsb3QoeGNvb3Jkcyh4KSx5Y29vcmRzKHgpLCAuLi4pOw0KICAgICAgICAgICAgICB4LjEgPC0gYyhiYm94KHgpWzFdLGJib3goeClbMl0sYmJveCh4KVsyXSxiYm94KHgpWzFdKTsNCiAgICAgICAgICAgICAgeS4xIDwtIGMoYmJveCh4KVszXSxiYm94KHgpWzNdLGJib3goeClbNF0sYmJveCh4KVs0XSk7DQogICAgICAgICAgICAgIHBvbHlnb24oeC4xLHkuMSl9IA0KICAgICAgICAgICAgZWxzZSB7cGxvdCh4Y29vcmRzKHgpLHljb29yZHMoeCksIC4uLil9fSkNCnBsb3QocHRzKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChwdHMsYmJveD1ULCBwY2g9MTksIGNvbD0iYmx1ZSIseGxhYj0ieCIsIHlsYWI9InkiKQ0KYGBgDQoNCiMgUGV3YXJpc2FuIENsYXNzDQpUZXJkYXBhdCBjbGFzcyBiYXJ1IHlhbmcgZGl0dXJ1bmthbiBkYXJpIGNvb3JkcyBkZW5nYW4gbWVuYW1iYWhrYW4gc2xvdCBuaWxhaQ0KYGBge3J9DQpzZXRDbGFzcygidmNvb3JkcyIsIHJlcHJlc2VudGF0aW9uKG5pbGFpID0gIm51bWVyaWMiKSwgY29udGFpbnMgPSAiY29vcmRzIikNCnZjb29yZHMgPC0gZnVuY3Rpb24oeCwgeSwgbmlsYWkpew0KICBpZiAoKGxlbmd0aCh4KSAhPSBsZW5ndGgoeSkpIHx8IChsZW5ndGgoeCkgIT0gbGVuZ3RoKG5pbGFpKSkpDQogICAgc3RvcCgibGVuZ3RoIHgsIHksIGRhbiBuaWxhaSBoYXJ1cyBiZXJuaWxhaSBzYW1hIikNCiAgaWYgKCFpcy5udW1lcmljKHgpIHx8ICFpcy5udW1lcmljKHkpIHx8ICFpcy5udW1lcmljKG5pbGFpKSkNCiAgICBzdG9wKCJ4LCB5LCBkYW4gbmlsYWkgaGFydXMgdmVrdG9yIG51bWVyaWMiKQ0KICBuZXcoInZjb29yZHMiLCB4ID0gYXMudmVjdG9yKHgpLCB5ID0gYXMudmVjdG9yKHkpLA0KICAgICAgbmlsYWkgPSBhcy52ZWN0b3IobmlsYWkpKQ0KfQ0KbmlsYWkgPC0gZnVuY3Rpb24ob2JqKSBvYmpAbmlsYWkNCnZwdHMgPC0gdmNvb3Jkcyh4Y29vcmRzKHB0cyksIHljb29yZHMocHRzKSwgcm91bmQoMTAwKnJ1bmlmKDUpKSkNCnZwdHMNCnB0cw0KYGBgDQoNCkthcmVuYSBhZGEgcGVuYW1iYWhhbiBzbG90IG5pbGFpIHNlaGluZ2dhIG1ldGhvZCB5YW5nIGRpd2FyaXNrYW4gcGVybHUgZGlkZWZpbmlzaSB1bGFuZyBzZXBlcnRpIGJlcmlrdXQuDQoNCmBNZXRob2QgU2hvd2ANCmBgYHtyfQ0Kc2V0TWV0aG9kKHNob3csIHNpZ25hdHVyZShvYmplY3QgPSAidmNvb3JkcyIpLA0KICAgICAgICAgIGZ1bmN0aW9uKG9iamVjdCkgDQogICAgICAgICAgICBwcmludChwYXN0ZSgiKCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0KHhjb29yZHMob2JqZWN0KSksICIsICIsDQogICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQoeWNvb3JkcyhvYmplY3QpKSwgIjsgIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdChuaWxhaShvYmplY3QpKSwiKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSIiKSwgDQogICAgICAgICAgICAgICAgICBxdW90ZT1GQUxTRSkpDQp2cHRzDQpwdHMNCmBgYA0KDQpgTWV0aG9kIFBsb3RgDQpgYGB7cn0NCnNldE1ldGhvZCgicGxvdCIsIHNpZ25hdHVyZSh4PSJ2Y29vcmRzIiksDQogICAgICAgICAgZnVuY3Rpb24oeCwgdHh0PUZBTFNFLCBiYm94PUZBTFNFLCAuLi4pew0KICAgICAgICAgICAgaWYgKGJib3gpIHsNCiAgICAgICAgICAgICAgaWYgKCF0eHQpIHtwbG90KHhjb29yZHMoeCkseWNvb3Jkcyh4KSwgLi4uKTt9IA0KICAgICAgICAgICAgICBlbHNlIHsNCiAgICAgICAgICAgICAgICBwbG90KHhjb29yZHMoeCkseWNvb3Jkcyh4KSwgdHlwZT0ibiIsIC4uLik7DQogICAgICAgICAgICAgICAgdGV4dCh4Y29vcmRzKHgpLHljb29yZHMoeCksIG5pbGFpKHgpLCAuLi4pfQ0KICAgICAgICAgICAgICB4LjEgPC0gYyhiYm94KHgpWzFdLGJib3goeClbMl0sYmJveCh4KVsyXSxiYm94KHgpWzFdKTsNCiAgICAgICAgICAgICAgeS4xIDwtIGMoYmJveCh4KVszXSxiYm94KHgpWzNdLGJib3goeClbNF0sYmJveCh4KVs0XSk7DQogICAgICAgICAgICAgIHBvbHlnb24oeC4xLHkuMSl9IA0KICAgICAgICAgICAgZWxzZSB7DQogICAgICAgICAgICAgIGlmICghdHh0KSB7cGxvdCh4Y29vcmRzKHgpLHljb29yZHMoeCksIC4uLil9DQogICAgICAgICAgICAgIGVsc2Ugew0KICAgICAgICAgICAgICAgIHBsb3QoeGNvb3Jkcyh4KSx5Y29vcmRzKHgpLCB0eXBlPSJuIiwgLi4uKTsNCiAgICAgICAgICAgICAgICB0ZXh0KHhjb29yZHMoeCkseWNvb3Jkcyh4KSwgbmlsYWkoeCksIC4uLik7fX0NCn0pDQpwbG90KHZwdHMpDQpgYGANCg0KYGBge3J9DQpwbG90KHZwdHMsIHR4dD1ULCBiYm94PVQsIHBjaD0xOSwgY29sPSJibHVlIikNCmBgYA0KDQojIFRyYW5zZm9ybWFzaSBkZW5nYW4gTWF0ZW1hdGlrDQpTZWJhZ2FpIGNvbnRvaCBkaWxha3VrYW4gdHJhbmZvcm1hc2kgdGVyaGFkYXAgbmlsYWkgZGVuZ2FuIGNvcyBiZXJpa3V0Lg0KYGBge3J9DQogc2V0TWV0aG9kKCJjb3MiLCBzaWduYXR1cmUoeCA9ICJ2Y29vcmRzIiksDQogZnVuY3Rpb24oeCkNCiB2Y29vcmRzKHhjb29yZHMoeCksDQogeWNvb3Jkcyh4KSwNCiBjb3MobmlsYWkoeCkpKSkNCiBjb3ModnB0cykNCmBgYA0KDQpTZXBlcnRpIHBhZGEgY2xhc3Mgc3lzdGVtIFMzLCB0ZXJkYXBhdCBncm91cCBNYXRoIHNlcGVydGkgYmVyaWt1dC4NCmBgYHtyfQ0Kc2V0TWV0aG9kKCJNYXRoIiwgc2lnbmF0dXJlKHggPSAidmNvb3JkcyIpLA0KICAgICAgICAgIGZ1bmN0aW9uKHgpIHZjb29yZHMoeGNvb3Jkcyh4KSwgeWNvb3Jkcyh4KSwgY2FsbEdlbmVyaWMobmlsYWkoeCkpKSkNCmNvcyh2cHRzKQ0Kc2luKHZwdHMpDQpzcXJ0KHZwdHMpDQpgYGANCg0KIyBPcGVyYXNpIEFyaXRtZXRpa2ENCkxhbmdrYWggZGFsYW0gbWVueXVzdW4gb3BlcmFzaSBhcml0bWV0aWthIG1pcmlwIGRlbmdhbiBzaXN0ZW0gUzMgeWFpdHUgbWVueXVzdW4gZnVuZ3NpIHVudHVrIGxva2FzaSB0ZXJsZWJpaCBkYWh1bHUuDQpgYGB7cn0NCnNhbWVsb2MgPC0gZnVuY3Rpb24oZTEsIGUyKSANCiAgKGxlbmd0aChuaWxhaShlMSkpID09IGxlbmd0aChuaWxhaShlMikpIHx8IGFueSh4Y29vcmRzKGUxKSA9PSB4Y29vcmRzKGUyKSkNCiAgfHwgYW55KHljb29yZHMoZTEpID09IHljb29yZHMoZTIpKSkNCmBgYA0KDQpsYWx1IG1lbnl1c3VuIG1ldGhvZCBhcml0bWV0aWthIGRlbmdhbiBrZWR1YSBvcGVyYW5kIG1lcnVwYWthbiBrZWxhcyB2Y29vcmRzLCBzZXBlcnRpIGJlcmlrdXQuDQpgYGB7cn0NCnNldE1ldGhvZCgiQXJpdGgiLCBzaWduYXR1cmUoZTEgPSAidmNvb3JkcyIsIGUyID0gInZjb29yZHMiKSwNCiAgICAgICAgICBmdW5jdGlvbihlMSwgZTIpew0KICAgICAgICAgICAgaWYgKCFzYW1lbG9jKGUxLCBlMikpDQogICAgICAgICAgICAgIHN0b3AoIkRpYnV0dWhrYW4gdGl0aWsgaWRlbnRpayIpDQogICAgICAgICAgICB2Y29vcmRzKHhjb29yZHMoZTEpLCB5Y29vcmRzKGUyKSwgDQogICAgICAgICAgICAgICAgICAgIGNhbGxHZW5lcmljKG5pbGFpKGUxKSwgbmlsYWkoZTIpKSkNCn0pDQp2cHRzDQp2cHRzK3ZwdHMNCjIrdnB0cw0KYGBgDQoNCmJpc2EgZGlsaWhhdCBwYWRhIHNhYXQgbWVuZ29wZXJhc2lrYW4gc3VhdHUgbnVtZXJpayBzZWJhZ2FpIG9wZXJhbmQsIG9wZXJhc2kgbWVuZ2FsYW1pIGVycm9yLiBNYWthIHBlcmx1IG1lbmFtYmFoa2FuIGBzZXRNZXRob2QoIkFyaXRoIiwuLi4pYCB1bnR1ayBvcGVyYXNpIGRlbmdhbiBudW1lcmlrIHNlcGVydGkgYmVyaWt1dC4NCiMjIE1lbmFtYmFoIG9wZXJhc2kgYXJpdG1ldGlrYSBkZW5nYW4gYXdhbCBudW1lcmlrDQpgYGB7cn0NCnNldE1ldGhvZCgiQXJpdGgiLCBzaWduYXR1cmUoZTEgPSAibnVtZXJpYyIsIGUyID0gInZjb29yZHMiKSwNCiAgICAgICAgICBmdW5jdGlvbihlMSwgZTIpew0KICAgICAgICAgICAgaWYgKGxlbmd0aChlMSk+bGVuZ3RoKG5pbGFpKGUyKSkpDQogICAgICAgICAgICAgIHN0b3AoImxlbmd0aCB5YW5nIHRpZGFrIGJlbmFyIikNCiAgICAgICAgICAgIHZjb29yZHMoeGNvb3JkcyhlMiksIHljb29yZHMoZTIpLCANCiAgICAgICAgICAgICAgICAgICAgY2FsbEdlbmVyaWMoYXMudmVjdG9yKGUxKSwgbmlsYWkoZTIpKSkNCn0pDQp2cHRzDQoyK3ZwdHMNCjIqdnB0cw0KYGBgDQoNCiMjIE1lbmFtYmFoIG9wZXJhc2kgYXJpdG1ldGlrYSBkZW5nYW4gYWtoaXIgbnVtZXJpaw0KYGBge3J9DQpzZXRNZXRob2QoIkFyaXRoIiwgc2lnbmF0dXJlKGUxID0gInZjb29yZHMiLCBlMiA9ICJudW1lcmljIiksDQogICAgICAgICAgZnVuY3Rpb24oZTEsIGUyKXsNCiAgICAgICAgICAgIGlmIChsZW5ndGgobmlsYWkoZTEpKSA8IGxlbmd0aChlMikpDQogICAgICAgICAgICAgIHN0b3AoImxlbmd0aCB5YW5nIHRpZGFrIGJlbmFyIikNCiAgICAgICAgICAgIHZjb29yZHMoeGNvb3JkcyhlMSksIHljb29yZHMoZTEpLCANCiAgICAgICAgICAgICAgICAgICAgY2FsbEdlbmVyaWMobmlsYWkoZTEpLCBhcy52ZWN0b3IoZTIpKSkNCn0pDQp2cHRzKzINCmBgYA0KDQojIE1ldGhvZCBTdWJzZXQNCk1lbmRlZmluaXNpa2FuIG1ldGhvZCB1bnR1ayBzdWJzZXQgZGVuZ2FuIHN5bnRheCBiZXJpa3V0Lg0KYGBge3J9DQpzZXRNZXRob2QoIlsiLCBzaWduYXR1cmUoeCA9ICJ2Y29vcmRzIiwgaSA9ICJBTlkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGogPSAibWlzc2luZyIsIGRyb3AgPSAibWlzc2luZyIpLA0KICAgICAgICAgIGZ1bmN0aW9uKHgsIGksIGopIHZjb29yZHMoeGNvb3Jkcyh4KVtpXSwgeWNvb3Jkcyh4KVtpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5pbGFpKHgpW2ldKSkNCnZwdHNbMTozXQ0KYGBgDQoNCiMgUGVtZXJpa3NhYW4gU3VhdHUgQ2xhc3MgT2JqZWsNClVudHVrIG1lbmdlY2VrIGFwYWthaCBzdWF0dSBvYmplayBtZXJ1cGFrYW4gc3VhdHUgY2xhc3MgZGlndW5ha2FuIGZ1bmdzaSBgaXMoKWAgYmVyaWt1dC4NCmBgYHtyfQ0KaXMocHRzLCJjb29yZHMiKQ0KaXMocHRzLCJ2Y29vcmRzIikNCmlzKHZwdHMsImNvb3JkcyIpDQppcyh2cHRzLCJ2Y29vcmRzIikNCmBgYA0KDQpVbnR1ayBtZW4tY29lcmNlIG9iamVrIGtlIG9iamVrIGxhaW4gZGFyaSBzdWF0dSBjbGFzcyBkaWd1bmFrYW4gZnVuZ3NpIGBhcygpYCBiZXJpa3V0Lg0KYGBge3J9DQphcyh2cHRzLCJjb29yZHMiKQ0KYGBgDQoNCm91dHB1dCBkaSBhdGFzIG1lbmFtcGlsa2FuIHZwdHMgc2ViYWdhaSBjb29yZHMsIHNlaGluZ2dhIGhhbnlhIG1lbmFtcGlsa2FuIHhjb29yZHMgZGFuIHljb29yZHMgdGFucGEgbmlsYWkuDQoNCmBgYHtyfQ0KYXMocHRzLCJ2Y29vcmRzIikNCmBgYA0KDQpvdXRwdXQgZGkgYXRhcyBtZW5hbXBpbGthbiBwdHMgc2ViYWdhaSB2Y29vcmRzLCBzZWhpbmdnYSBtZW5hbXBpbGthbiB4Y29vcmRzLCB5Y29vcmRzLCBkYW4gbmlsYWkuIE5hbXVuIGthcmVuYSBwdHMgdGlkYWsgbWVtaWxpa2kgYXRyaWJ1dCBuaWxhaSBtYWthIGRpdGFtcGlsa2FuIHNlYmFnYWkgYmxhbmtzcGFjZSBzZXRlbGFoIHRhbmRhIDs=