Pemrograman Berorientasi Objek
Pemrograman berorientasi objek merupakan sebuah paradigma dalam pembuatan sebuah program. PBO menitikberatkan pada identifikasi objek-objek yang terlibat dalam sebuah program dan bagaimana objek-objek tersebut berinterakasi. Pada PBO, program yang dibangun akan dibagi-bagi menjadi objek-objek. Prinsip dasar dari PBO adalah abstraksi, enkapsulasi, inheritance (pewarisan), dan polymorphism. PBO menyediakan class dan object sebagai alat dasar untuk meminimalisir dan mengatur kompleksitas dari program.
Class (kelas)
Merupakan definisi statik (kerangka dasar) dari objek yang akan diciptakan. Suatu class dibagi menjadi:
- Property : data atau state yang dimiliki oleh class. Contoh pada class Mobil, memiliki property: warna, Model, Produsen.
- Method : behavior (perilaku) sebuah class. Bisa dikatakan sebagai aksi atau tindakan yang bisa dilakukan oleh suatu class. Contoh pada class Mobil, memiliki method: Start, Stop, Change Gear, Turn.
Object
Objek adalah komponen yang diciptakan dari class (instance of class). Satu class bisa menghasilkan banyak objek. Proses untuk membuat sebuah objek disebut instantiation. Setiap objek memiliki karakteristik dan fitur masing masing. Objek memiliki siklus creation, manipulation, dan destruction.
Pengecekan objek data pada R bisa menggunakan fungsi is.object() Vektor
[1] TRUE
[1] "integer"
[1] FALSE
Matriks
matriks = matrix(1:20,ncol=4,byrow = T)
is.matrix(matriks)
[1] TRUE
[1] "integer"
[1] FALSE
Array
arai<-array(c(1:10),c(2,5))
is.array(arai)
[1] TRUE
[1] "integer"
[1] FALSE
Data frame
a<-1:12
b<-letters[1:12]
data1<-data.frame(a,b)
is.data.frame(data1)
[1] TRUE
[1] "list"
[1] TRUE
Metode
set.seed(55)
y<-10+a+rnorm(12)
f.y<-lm(y~a)
typeof(f.y)
[1] "list"
[1] TRUE
Hasil suatu metode dapat dikenali sebagai objek di R.
Objek yang dianggap oleh R setidaknya memiliki tipe data/ typeof(nama.objek) = “list”. Adapun tingkatan tipe data dalam R (dari rendah ke tinggi) adalah sebagai berikut: vector < matrix < array < list < data frame
Pemrograman Berorientasi Objek di R
R telah mengimplementasikan pemrograman berorientasi objek. Semua dalam R adalah objek. Pengembangan awal objek di R menggunakan Class System S3 yang tidak terlalu ketat. Pendefinisian yang ketat secara formal, R menggunakan Class System S4.
Objek: Class System S3
Suatu class dalam S3 tidak didefinisikan dengan ketat. Fungsi class digunakan untuk menjadikan sebuah objek menjadi class yang diinginkan.
pts <- list (x=round(rnorm(5),2), y=round(rnorm(5),2))
class(pts)
[1] "list"
$x
[1] 1.13 -0.41 -2.00 -2.27 -0.11
$y
[1] 0.78 -0.67 -0.69 0.91 -0.27
Menjadikan pts sebagai class baru secara langsung:
class(pts) <- "coords"
class(pts)
[1] "coords"
$x
[1] 1.13 -0.41 -2.00 -2.27 -0.11
$y
[1] 0.78 -0.67 -0.69 0.91 -0.27
attr(,"class")
[1] "coords"
Langkah sederhana membuat objek di atas sangat tidak dianjurkan karena nilai-nilai instan-nya mungkin tidak tepat.
Konstruktor dan Aksesor
Fungsi konstruktor dibutuhkan untuk mengecek instan sesuai dengan objek, sementara Fungsi aksesor dibutuhkan untuk mengakses data pada suatu class.
Konstruktor
Misalnya berikut adalah ketentuan class coords
- x dan y harus berupa numerik
- vektor tidak boleh NA, NaN, Inf
- vektor harus memiliki panjang yang sama
Fungsi konstruktor untuk membuat class coords:
coords <- function(x, y) {
if (!is.numeric(x) || !is.numeric(y) ||
!all(is.finite(x)) || !all(is.finite(y)))
stop("Titik koordinat tidak tepat!")
if (length(x) != length(y))
stop("Panjang koordinat berbeda")
pts <- list(x=x, y=y)
class(pts) = "coords"
pts
}
pts <- coords(x = round(rnorm(5),2), y = round(rnorm(5),2))
pts
$x
[1] 0.31 -0.59 1.08 -0.96 -0.78
$y
[1] -1.16 1.50 -0.50 -0.31 1.68
attr(,"class")
[1] "coords"
Aksesor
Mengakses data dalam class coords dapat menggunakan akses objek awalnya(list)
$x
[1] 0.31 -0.59 1.08 -0.96 -0.78
$y
[1] -1.16 1.50 -0.50 -0.31 1.68
Tetapi secara formal tidak dianjurkan mengakses data secara langsung (seperti contoh di atas).
Sehingga diperlukan fungsi aksesor untuk mengakses data pada class coords seperti berikut.
xcoords <- function(obj) obj$x
ycoords <- function(obj) obj$y
xcoords(pts)
[1] 0.31 -0.59 1.08 -0.96 -0.78
[1] -1.16 1.50 -0.50 -0.31 1.68
Fungsi Generik
Fungsi generik merupakan suatu method dari suatu class objek dalam R. Syntax untuk mendefinisikan suatu fungsi generik: method.class <- function() ekspresibaru . Terdapat beberapa fungsi generik yang sudah ada seperti berikut.
Method Print
Method print merupakan cara menampilkan data pada suatu objek Class System S3.
print.coords <- function(obj) {
print(paste("(",
format(xcoords(obj)),
", ",
format(ycoords(obj)),
")", sep=""),
quote=FALSE)
}
pts
[1] ( 0.31, -1.16) (-0.59, 1.50) ( 1.08, -0.50) (-0.96, -0.31)
[5] (-0.78, 1.68)
Method Length
Fungsi length menghitung banyaknya anggota dari objek.
[1] 2
Penggunaan length di atas kurang tepat karena length akan menghitung banyaknya anggota list. Sehingga perlu mendefinisikan ulang fungsi length untuk class coords:
length.coords <- function(obj) length(xcoords(obj))
length(pts)
[1] 5
Membuat Fungsi Generik Baru
bbox <- function(obj)
UseMethod("bbox") #menjadikan bbox sebagai fungsi generik
bbox.coords <- function(obj) {
matrix(c(mean(xcoords(obj)),
mean(ycoords(obj))),
nc = 2, dimnames = list(
c("mean"),
c("x:", "y:")))
}
bbox(pts)
x: y:
mean -0.188 0.242
Method Plot
Berikut adalah fungsi untuk membuat plot khusus untuk class coords:
plot.coords <- function(obj, bbox=FALSE, ...) {
if (bbox) {
plot(xcoords(obj),ycoords(obj), ...);
x <- c(bbox(obj)[1],bbox(obj)[2],
bbox(obj)[2],bbox(obj)[1]);
y <- c(bbox(obj)[3],bbox(obj)[3],
bbox(obj)[4],bbox(obj)[4]);
polygon(x,y)
} else {
plot(xcoords(obj),ycoords(obj), ...)
}
}
plot(pts)

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

Pewarisan Class
Diinginkan sebuah objek yang berisi lokasi (coords) dan terdapat nilai pada lokasi tersebut. Diperlukan menciptakan class baru vcoords sebagai turunan dari coords. Fungsi konstruktor dari class vcoords:
vcoords <- function(x, y, v) {
if (!is.numeric(x) || !is.numeric(y) || !is.numeric(v) ||
!all(is.finite(x)) || !all(is.finite(y)))
stop("Titik koordinat tidak tepat!")
if (length(x) != length(y) || length(x) != length(v))
stop("Panjang koordinat berbeda")
pts <- list(x=x, y=y, v=v)
class(pts) = c("vcoords", "coords")
pts
}
nilai <- function(obj) obj$v
vpts <- vcoords(x = round(rnorm(5), 2),
y = round(rnorm(5), 2),
v = round(runif(5,0,100)))
vpts
[1] ( 0.03, -0.55) ( 0.65, 1.39) (-0.34, 2.20) ( 1.25, -1.53)
[5] ( 0.29, 1.28)
Fungsi xcoords, ycoords, dan method bbox dari kelas coords masih sama sehingga tidak perlu didefinisi ulang (bisa langsung digunakan pada class vcoords)
[1] 0.03 0.65 -0.34 1.25 0.29
[1] -0.55 1.39 2.20 -1.53 1.28
x: y:
mean 0.376 0.558
Method print juga diwariskan dari class coords, namun karena ada tambahan nilai sehingga perlu didefinisi ulang sebagai berikut.
print.vcoords <- function(obj) {
print(paste("(",
format(xcoords(obj)),
", ",
format(ycoords(obj)),
": ", format(nilai(obj)),
")", sep=""),
quote=FALSE)
}
vpts
[1] ( 0.03, -0.55: 96) ( 0.65, 1.39: 68) (-0.34, 2.20: 52)
[4] ( 1.25, -1.53: 96) ( 0.29, 1.28: 66)
Begitu pula dengan method plot perlu didefinisi ulang karena ada tambahan nilai seperti berikut.
plot.vcoords <- function(obj, txt=FALSE, bbox=FALSE,...) {
if (bbox) {
if (!txt) {
plot(xcoords(obj),ycoords(obj),...);
} else {
plot(xcoords(obj),ycoords(obj),type="n",...);
text(xcoords(obj),ycoords(obj),nilai(obj),...);
}
x <- c(bbox(pts)[1],bbox(pts)[2],bbox(pts)[2],bbox(pts)[1]);
y <- c(bbox(pts)[3],bbox(pts)[3],bbox(pts)[4],bbox(pts)[4]);
polygon(x,y)
} else {
if (!txt) {
plot(xcoords(obj),ycoords(obj),...);
} else {
plot(xcoords(obj),ycoords(obj),type="n",...);
text(xcoords(obj),ycoords(obj),nilai(obj),...);
}
}
}
plot(vpts)

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

Operasi Aritmetika Method Groups Ops
Method Group Ops merupakan fungsi generik yang digunakan untuk mendefinisikan semua operasi biner (operasi antar 2 operand) dengan operator sebagai berikut:
"+", "-", "*", "/", "^", "%%", "%/%"
"&", "|", "!"
"==", "!=", "<", "<=", ">=", ">"
Supaya berjalan dengan tepat diperlukan lokasi dari vcoords yang dioperasikan adalah identik
sameloc <- function(e1,e2) {
(length(nilai(e1))==length(nilai(e2))
|| all(xcoords(e1)==xcoords(e2))
|| all(ycoords(e1)==ycoords(e2))
)
}
Ops.vcoords <- function(e1,e2) {
if (!sameloc(e1,e2))
stop ("Lokasi berbeda")
else vcoords(xcoords(e1),
ycoords(e1),
get(.Generic) (nilai(e1),nilai(e2)))
}
vpts
[1] ( 0.03, -0.55: 96) ( 0.65, 1.39: 68) (-0.34, 2.20: 52)
[4] ( 1.25, -1.53: 96) ( 0.29, 1.28: 66)
[1] ( 0.03, -0.55: 192) ( 0.65, 1.39: 136) (-0.34, 2.20: 104)
[4] ( 1.25, -1.53: 192) ( 0.29, 1.28: 132)
[1] ( 0.03, -0.55: 9216) ( 0.65, 1.39: 4624) (-0.34, 2.20: 2704)
[4] ( 1.25, -1.53: 9216) ( 0.29, 1.28: 4356)
Method Subset
Method subset dapat diakses sesuai dengan kebutuhan, misal kondisi yang diinginkan: vpts[xcoords(vpts) < 0 & ycoords(vpts) < 0] Dapat ditangani dengan mendefinisikan method berikut.
`[.vcoords` <- function(x, i) {
vcoords(xcoords(x)[i], ycoords(x)[i],
nilai(x)[i])
}
vpts
[1] ( 0.03, -0.55: 96) ( 0.65, 1.39: 68) (-0.34, 2.20: 52)
[4] ( 1.25, -1.53: 96) ( 0.29, 1.28: 66)
[1] ( 0.03, -0.55: 96) ( 0.65, 1.39: 68) (-0.34, 2.20: 52)
vpts[xcoords(vpts) < 0 & ycoords(vpts) < 0]
[1] (, : )
Pemeriksaan Suatu Class Objek
untuk mengecek apakah suatu objek merupakan suatu class digunakan fungsi inherits seperti berikut.
[1] TRUE
[1] FALSE
[1] TRUE
[1] TRUE
terlihat bahwa objek pts tidak termasuk ke dalam class vcoords. Sedangkan objek vpts termasuk ke dalam class coords, karena vpts merupakan turunan dari pts sehingga mendapat warisan (inheritance) class dari objek pts.
Penutup Class System S3
Class system S3 memberikan fasilitas object-oriented yang terlalu longgar sehingga banyak technical issue dalam sistem class ini. Sebagai contoh ekspresi berikut diperbolehkan dalam R
model <- 1:10
class(model) <- "lm"
class(model)
[1] "lm"
padahal class “lm” merupakan class untuk pemodelan linear.
LS0tDQp0aXRsZTogIlBlbXJvZ3JhbWFuIEJlcm9yaWVudGFzaSBPYmplayBkYWxhbSBSIENsYXNzIFN5c3RlbSBTMyINCmF1dGhvcjogIkFubmlzc2EgTnVyIEZpdHJpYSBGYXRoaW5hXltNYWhhc2lzd2EgU3RhdGlzdGlrYSBkYW4gU2FpbnMgRGF0YSBJUEIsIGFubmlzc2FfbmZmQGFwcHMuaXBiLmFjLmlkXSINCmRhdGU6ICIyLzQvMjAyMiINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0aGVtZTogY29zbW8NCiAgICBoaWdobGlnaHQ6IGthdGUNCmVkaXRvcl9vcHRpb25zOiANCiAgbWFya2Rvd246IA0KICAgIHdyYXA6IHNlbnRlbmNlDQotLS0NCg0KIyBQZW1yb2dyYW1hbiBCZXJvcmllbnRhc2kgT2JqZWsNCg0KUGVtcm9ncmFtYW4gYmVyb3JpZW50YXNpIG9iamVrIG1lcnVwYWthbiBzZWJ1YWggcGFyYWRpZ21hIGRhbGFtIHBlbWJ1YXRhbiBzZWJ1YWggcHJvZ3JhbS4NClBCTyBtZW5pdGlrYmVyYXRrYW4gcGFkYSBpZGVudGlmaWthc2kgb2JqZWstb2JqZWsgeWFuZyB0ZXJsaWJhdCBkYWxhbSBzZWJ1YWggcHJvZ3JhbSBkYW4gYmFnYWltYW5hIG9iamVrLW9iamVrIHRlcnNlYnV0IGJlcmludGVyYWthc2kuDQpQYWRhIFBCTywgcHJvZ3JhbSB5YW5nIGRpYmFuZ3VuIGFrYW4gZGliYWdpLWJhZ2kgbWVuamFkaSBvYmplay1vYmplay4NClByaW5zaXAgZGFzYXIgZGFyaSBQQk8gYWRhbGFoIGFic3RyYWtzaSwgZW5rYXBzdWxhc2ksIGluaGVyaXRhbmNlIChwZXdhcmlzYW4pLCBkYW4gcG9seW1vcnBoaXNtLg0KUEJPIG1lbnllZGlha2FuICpjbGFzcyogZGFuICpvYmplY3QqIHNlYmFnYWkgYWxhdCBkYXNhciB1bnR1ayBtZW1pbmltYWxpc2lyIGRhbiBtZW5nYXR1ciBrb21wbGVrc2l0YXMgZGFyaSBwcm9ncmFtLg0KDQojIyBDbGFzcyAoa2VsYXMpDQoNCk1lcnVwYWthbiBkZWZpbmlzaSBzdGF0aWsgKGtlcmFuZ2thIGRhc2FyKSBkYXJpIG9iamVrIHlhbmcgYWthbiBkaWNpcHRha2FuLg0KU3VhdHUgY2xhc3MgZGliYWdpIG1lbmphZGk6DQoNCi0gICAqUHJvcGVydHkqIDogZGF0YSBhdGF1ICpzdGF0ZSogeWFuZyBkaW1pbGlraSBvbGVoICpjbGFzcyouIENvbnRvaCBwYWRhIGNsYXNzIE1vYmlsLCBtZW1pbGlraSBwcm9wZXJ0eTogd2FybmEsIE1vZGVsLCBQcm9kdXNlbi4NCi0gICAqTWV0aG9kKiA6ICpiZWhhdmlvciogKHBlcmlsYWt1KSBzZWJ1YWggKmNsYXNzKi4gQmlzYSBkaWthdGFrYW4gc2ViYWdhaSBha3NpIGF0YXUgdGluZGFrYW4geWFuZyBiaXNhIGRpbGFrdWthbiBvbGVoIHN1YXR1IGNsYXNzLiBDb250b2ggcGFkYSBjbGFzcyBNb2JpbCwgbWVtaWxpa2kgbWV0aG9kOiBTdGFydCwgU3RvcCwgQ2hhbmdlIEdlYXIsIFR1cm4uDQoNCiMjIE9iamVjdA0KDQpPYmplayBhZGFsYWgga29tcG9uZW4geWFuZyBkaWNpcHRha2FuIGRhcmkgY2xhc3MgKihpbnN0YW5jZSBvZiBjbGFzcykqLg0KU2F0dSBjbGFzcyBiaXNhIG1lbmdoYXNpbGthbiBiYW55YWsgb2JqZWsuDQpQcm9zZXMgdW50dWsgbWVtYnVhdCBzZWJ1YWggb2JqZWsgZGlzZWJ1dCBpbnN0YW50aWF0aW9uLg0KU2V0aWFwIG9iamVrIG1lbWlsaWtpIGthcmFrdGVyaXN0aWsgZGFuIGZpdHVyIG1hc2luZyBtYXNpbmcuDQpPYmplayBtZW1pbGlraSBzaWtsdXMgY3JlYXRpb24sIG1hbmlwdWxhdGlvbiwgZGFuIGRlc3RydWN0aW9uLg0KDQpQZW5nZWNla2FuIG9iamVrIGRhdGEgcGFkYSBSIGJpc2EgbWVuZ2d1bmFrYW4gZnVuZ3NpIGBpcy5vYmplY3QoKWAgDQoqVmVrdG9yKg0KYGBge3J9DQpBPC1jKDE6MTApDQppcy52ZWN0b3IoQSkNCnR5cGVvZihBKQ0KaXMub2JqZWN0KEEpDQpgYGANCg0KKk1hdHJpa3MqDQpgYGB7cn0NCm1hdHJpa3MgPSBtYXRyaXgoMToyMCxuY29sPTQsYnlyb3cgPSBUKQ0KaXMubWF0cml4KG1hdHJpa3MpDQp0eXBlb2YobWF0cmlrcykNCmlzLm9iamVjdChtYXRyaWtzKQ0KYGBgDQoNCipBcnJheSoNCmBgYHtyfQ0KYXJhaTwtYXJyYXkoYygxOjEwKSxjKDIsNSkpIA0KaXMuYXJyYXkoYXJhaSkgDQp0eXBlb2YoYXJhaSkNCmlzLm9iamVjdChhcmFpKQ0KYGBgDQoNCipEYXRhIGZyYW1lKg0KYGBge3J9DQphPC0xOjEyDQpiPC1sZXR0ZXJzWzE6MTJdDQpkYXRhMTwtZGF0YS5mcmFtZShhLGIpDQppcy5kYXRhLmZyYW1lKGRhdGExKQ0KdHlwZW9mKGRhdGExKQ0KaXMub2JqZWN0KGRhdGExKQ0KYGBgDQoNCipNZXRvZGUqDQpgYGB7cn0NCnNldC5zZWVkKDU1KQ0KeTwtMTArYStybm9ybSgxMikNCmYueTwtbG0oeX5hKQ0KdHlwZW9mKGYueSkNCmlzLm9iamVjdChmLnkpDQpgYGANCkhhc2lsIHN1YXR1IG1ldG9kZSBkYXBhdCBkaWtlbmFsaSBzZWJhZ2FpIG9iamVrIGRpIFIuDQoNCk9iamVrIHlhbmcgZGlhbmdnYXAgb2xlaCBSIHNldGlkYWtueWEgbWVtaWxpa2kgdGlwZSBkYXRhLyB0eXBlb2YobmFtYS5vYmplaykgPSAibGlzdCIuIA0KQWRhcHVuIHRpbmdrYXRhbiB0aXBlIGRhdGEgZGFsYW0gUiAoZGFyaSByZW5kYWgga2UgdGluZ2dpKSBhZGFsYWggc2ViYWdhaSBiZXJpa3V0Og0KdmVjdG9yIDwgbWF0cml4IDwgYXJyYXkgPCBsaXN0IDwgZGF0YSBmcmFtZQ0KDQojIyBQZW1yb2dyYW1hbiBCZXJvcmllbnRhc2kgT2JqZWsgZGkgUg0KUiB0ZWxhaCBtZW5naW1wbGVtZW50YXNpa2FuIHBlbXJvZ3JhbWFuIGJlcm9yaWVudGFzaSBvYmplay4gU2VtdWEgZGFsYW0gUiBhZGFsYWggb2JqZWsuIFBlbmdlbWJhbmdhbiBhd2FsIG9iamVrIGRpIFIgbWVuZ2d1bmFrYW4gKkNsYXNzIFN5c3RlbSBTMyogeWFuZyB0aWRhayB0ZXJsYWx1IGtldGF0LiBQZW5kZWZpbmlzaWFuIHlhbmcga2V0YXQgc2VjYXJhIGZvcm1hbCwgUiBtZW5nZ3VuYWthbiAqQ2xhc3MgU3lzdGVtIFM0Ki4NCg0KIyBPYmplazogQ2xhc3MgU3lzdGVtIFMzIA0KU3VhdHUgY2xhc3MgZGFsYW0gUzMgdGlkYWsgZGlkZWZpbmlzaWthbiBkZW5nYW4ga2V0YXQuDQpGdW5nc2kgYGNsYXNzYCBkaWd1bmFrYW4gdW50dWsgbWVuamFkaWthbiBzZWJ1YWggb2JqZWsgbWVuamFkaSBjbGFzcyB5YW5nIGRpaW5naW5rYW4uDQpgYGB7cn0NCnB0cyA8LSBsaXN0ICh4PXJvdW5kKHJub3JtKDUpLDIpLCB5PXJvdW5kKHJub3JtKDUpLDIpKQ0KY2xhc3MocHRzKQ0KcHRzDQpgYGANCg0KTWVuamFkaWthbiBgcHRzYCBzZWJhZ2FpIGNsYXNzIGJhcnUgc2VjYXJhIGxhbmdzdW5nOg0KYGBge3J9DQpjbGFzcyhwdHMpIDwtICJjb29yZHMiDQpjbGFzcyhwdHMpDQpwdHMNCmBgYA0KDQpMYW5na2FoIHNlZGVyaGFuYSBtZW1idWF0IG9iamVrIGRpIGF0YXMgc2FuZ2F0IHRpZGFrIGRpYW5qdXJrYW4ga2FyZW5hIG5pbGFpLW5pbGFpIGluc3Rhbi1ueWEgbXVuZ2tpbiB0aWRhayB0ZXBhdC4NCg0KIyBLb25zdHJ1a3RvciBkYW4gQWtzZXNvcg0KRnVuZ3NpIGtvbnN0cnVrdG9yIGRpYnV0dWhrYW4gdW50dWsgbWVuZ2VjZWsgaW5zdGFuIHNlc3VhaSBkZW5nYW4gb2JqZWssIHNlbWVudGFyYSBGdW5nc2kgYWtzZXNvciBkaWJ1dHVoa2FuIHVudHVrIG1lbmdha3NlcyBkYXRhIHBhZGEgc3VhdHUgY2xhc3MuDQoNCiMjIEtvbnN0cnVrdG9yDQpNaXNhbG55YSBiZXJpa3V0IGFkYWxhaCBrZXRlbnR1YW4gY2xhc3MgY29vcmRzDQoNCi0gICB4IGRhbiB5IGhhcnVzIGJlcnVwYSBudW1lcmlrDQotICAgdmVrdG9yIHRpZGFrIGJvbGVoIE5BLCBOYU4sIEluZg0KLSAgIHZla3RvciBoYXJ1cyBtZW1pbGlraSBwYW5qYW5nIHlhbmcgc2FtYQ0KDQpGdW5nc2kga29uc3RydWt0b3IgdW50dWsgbWVtYnVhdCBjbGFzcyBjb29yZHM6DQpgYGB7cn0NCmNvb3JkcyA8LSBmdW5jdGlvbih4LCB5KSB7DQogIGlmICghaXMubnVtZXJpYyh4KSB8fCAhaXMubnVtZXJpYyh5KSB8fCANCiAgICAgICFhbGwoaXMuZmluaXRlKHgpKSB8fCAhYWxsKGlzLmZpbml0ZSh5KSkpDQogICAgc3RvcCgiVGl0aWsga29vcmRpbmF0IHRpZGFrIHRlcGF0ISIpDQogIGlmIChsZW5ndGgoeCkgIT0gbGVuZ3RoKHkpKQ0KICAgIHN0b3AoIlBhbmphbmcga29vcmRpbmF0IGJlcmJlZGEiKQ0KICBwdHMgPC0gbGlzdCh4PXgsIHk9eSkNCiAgY2xhc3MocHRzKSA9ICJjb29yZHMiDQogIHB0cw0KfQ0KcHRzIDwtIGNvb3Jkcyh4ID0gcm91bmQocm5vcm0oNSksMiksIHkgPSByb3VuZChybm9ybSg1KSwyKSkNCnB0cw0KDQpgYGANCg0KIyMgQWtzZXNvcg0KTWVuZ2Frc2VzIGRhdGEgZGFsYW0gY2xhc3MgYGNvb3Jkc2AgZGFwYXQgbWVuZ2d1bmFrYW4gYWtzZXMgb2JqZWsgYXdhbG55YShsaXN0KQ0KYGBge3J9DQpwdHNbMV0NCnB0c1syXQ0KYGBgDQoNClRldGFwaSBzZWNhcmEgZm9ybWFsIHRpZGFrIGRpYW5qdXJrYW4gbWVuZ2Frc2VzIGRhdGEgc2VjYXJhIGxhbmdzdW5nIChzZXBlcnRpIGNvbnRvaCBkaSBhdGFzKS4NCg0KU2VoaW5nZ2EgZGlwZXJsdWthbiBmdW5nc2kgYWtzZXNvciB1bnR1ayBtZW5nYWtzZXMgZGF0YSBwYWRhIGNsYXNzIGBjb29yZHNgIHNlcGVydGkgYmVyaWt1dC4NCmBgYHtyfQ0KeGNvb3JkcyA8LSBmdW5jdGlvbihvYmopIG9iaiR4DQp5Y29vcmRzIDwtIGZ1bmN0aW9uKG9iaikgb2JqJHkNCnhjb29yZHMocHRzKQ0KeWNvb3JkcyhwdHMpDQpgYGANCg0KIyBGdW5nc2kgR2VuZXJpaw0KRnVuZ3NpIGdlbmVyaWsgbWVydXBha2FuIHN1YXR1IG1ldGhvZCBkYXJpIHN1YXR1IGNsYXNzIG9iamVrIGRhbGFtIFIuIFN5bnRheCB1bnR1ayBtZW5kZWZpbmlzaWthbiBzdWF0dSBmdW5nc2kgZ2VuZXJpazoNCmBtZXRob2QuY2xhc3MgPC0gZnVuY3Rpb24oKSBla3NwcmVzaWJhcnVgIC4gDQpUZXJkYXBhdCBiZWJlcmFwYSBmdW5nc2kgZ2VuZXJpayB5YW5nIHN1ZGFoIGFkYSBzZXBlcnRpIGJlcmlrdXQuDQoNCiMjIE1ldGhvZCBQcmludA0KTWV0aG9kIHByaW50IG1lcnVwYWthbiBjYXJhIG1lbmFtcGlsa2FuIGRhdGEgcGFkYSBzdWF0dSBvYmplayBDbGFzcyBTeXN0ZW0gUzMuDQpgYGB7cn0NCnByaW50LmNvb3JkcyA8LSBmdW5jdGlvbihvYmopIHsNCiAgcHJpbnQocGFzdGUoIigiLA0KICAgICAgICAgICAgICBmb3JtYXQoeGNvb3JkcyhvYmopKSwNCiAgICAgICAgICAgICAgIiwgIiwNCiAgICAgICAgICAgICAgZm9ybWF0KHljb29yZHMob2JqKSksDQogICAgICAgICAgICAgICIpIiwgc2VwPSIiKSwNCiAgICAgICAgcXVvdGU9RkFMU0UpDQp9DQpwdHMNCmBgYA0KDQojIyBNZXRob2QgTGVuZ3RoDQpGdW5nc2kgbGVuZ3RoIG1lbmdoaXR1bmcgYmFueWFrbnlhIGFuZ2dvdGEgZGFyaSBvYmplay4NCmBgYHtyfQ0KbGVuZ3RoKHB0cykNCmBgYA0KUGVuZ2d1bmFhbiBsZW5ndGggZGkgYXRhcyBrdXJhbmcgdGVwYXQga2FyZW5hIGxlbmd0aCBha2FuIG1lbmdoaXR1bmcgYmFueWFrbnlhIGFuZ2dvdGEgbGlzdC4NClNlaGluZ2dhIHBlcmx1IG1lbmRlZmluaXNpa2FuIHVsYW5nIGZ1bmdzaSBsZW5ndGggdW50dWsgY2xhc3MgY29vcmRzOg0KYGBge3J9DQpsZW5ndGguY29vcmRzIDwtIGZ1bmN0aW9uKG9iaikgbGVuZ3RoKHhjb29yZHMob2JqKSkNCmxlbmd0aChwdHMpDQpgYGANCg0KIyMgTWVtYnVhdCBGdW5nc2kgR2VuZXJpayBCYXJ1DQpgYGB7cn0NCmJib3ggPC0gZnVuY3Rpb24ob2JqKQ0KICBVc2VNZXRob2QoImJib3giKSAjbWVuamFkaWthbiBiYm94IHNlYmFnYWkgZnVuZ3NpIGdlbmVyaWsNCg0KDQpiYm94LmNvb3JkcyA8LSBmdW5jdGlvbihvYmopIHsNCiAgbWF0cml4KGMobWVhbih4Y29vcmRzKG9iaikpLA0KICAgICAgICAgICBtZWFuKHljb29yZHMob2JqKSkpLA0KICAgICAgICAgbmMgPSAyLCBkaW1uYW1lcyA9IGxpc3QoDQogICAgICAgICAgIGMoIm1lYW4iKSwNCiAgICAgICAgICAgYygieDoiLCAieToiKSkpDQp9DQpiYm94KHB0cykNCmBgYA0KDQojIyBNZXRob2QgUGxvdA0KQmVyaWt1dCBhZGFsYWggZnVuZ3NpIHVudHVrIG1lbWJ1YXQgcGxvdCBraHVzdXMgdW50dWsgY2xhc3MgY29vcmRzOg0KYGBge3J9DQpwbG90LmNvb3JkcyA8LSBmdW5jdGlvbihvYmosIGJib3g9RkFMU0UsIC4uLikgew0KICBpZiAoYmJveCkgew0KICAgIHBsb3QoeGNvb3JkcyhvYmopLHljb29yZHMob2JqKSwgLi4uKTsNCiAgICB4IDwtIGMoYmJveChvYmopWzFdLGJib3gob2JqKVsyXSwNCiAgICAgICAgICAgYmJveChvYmopWzJdLGJib3gob2JqKVsxXSk7DQogICAgeSA8LSBjKGJib3gob2JqKVszXSxiYm94KG9iailbM10sDQogICAgICAgICAgIGJib3gob2JqKVs0XSxiYm94KG9iailbNF0pOw0KICAgIHBvbHlnb24oeCx5KQ0KICB9IGVsc2Ugew0KICAgIHBsb3QoeGNvb3JkcyhvYmopLHljb29yZHMob2JqKSwgLi4uKQ0KICB9DQp9DQpwbG90KHB0cykNCmBgYA0KDQpgYGB7cn0NCg0KcGxvdChwdHMsIGJib3g9VCxwY2g9MTksY29sPSJibHVlIikNCmBgYA0KDQojIFBld2FyaXNhbiBDbGFzcw0KRGlpbmdpbmthbiBzZWJ1YWggb2JqZWsgeWFuZyBiZXJpc2kgbG9rYXNpIChjb29yZHMpIGRhbiB0ZXJkYXBhdCBuaWxhaSBwYWRhIGxva2FzaSB0ZXJzZWJ1dC4NCkRpcGVybHVrYW4gbWVuY2lwdGFrYW4gY2xhc3MgYmFydSB2Y29vcmRzIHNlYmFnYWkgdHVydW5hbiBkYXJpIGNvb3Jkcy4NCkZ1bmdzaSBrb25zdHJ1a3RvciBkYXJpIGNsYXNzIHZjb29yZHM6DQpgYGB7cn0NCnZjb29yZHMgPC0gZnVuY3Rpb24oeCwgeSwgdikgew0KICBpZiAoIWlzLm51bWVyaWMoeCkgfHwgIWlzLm51bWVyaWMoeSkgfHwgIWlzLm51bWVyaWModikgfHwgDQogICAgICAhYWxsKGlzLmZpbml0ZSh4KSkgfHwgIWFsbChpcy5maW5pdGUoeSkpKQ0KICAgIHN0b3AoIlRpdGlrIGtvb3JkaW5hdCB0aWRhayB0ZXBhdCEiKQ0KICBpZiAobGVuZ3RoKHgpICE9IGxlbmd0aCh5KSB8fCBsZW5ndGgoeCkgIT0gbGVuZ3RoKHYpKQ0KICAgIHN0b3AoIlBhbmphbmcga29vcmRpbmF0IGJlcmJlZGEiKQ0KICBwdHMgPC0gbGlzdCh4PXgsIHk9eSwgdj12KQ0KICBjbGFzcyhwdHMpID0gYygidmNvb3JkcyIsICJjb29yZHMiKQ0KICBwdHMNCn0NCm5pbGFpIDwtIGZ1bmN0aW9uKG9iaikgb2JqJHYNCnZwdHMgPC0gdmNvb3Jkcyh4ID0gcm91bmQocm5vcm0oNSksIDIpLA0KICAgICAgICAgICAgICAgIHkgPSByb3VuZChybm9ybSg1KSwgMiksDQogICAgICAgICAgICAgICAgdiA9IHJvdW5kKHJ1bmlmKDUsMCwxMDApKSkNCnZwdHMNCmBgYA0KDQpGdW5nc2kgeGNvb3JkcywgeWNvb3JkcywgZGFuIG1ldGhvZCBiYm94IGRhcmkga2VsYXMgY29vcmRzIG1hc2loIHNhbWEgc2VoaW5nZ2EgdGlkYWsgcGVybHUgZGlkZWZpbmlzaSB1bGFuZyAoYmlzYSBsYW5nc3VuZyBkaWd1bmFrYW4gcGFkYSBjbGFzcyB2Y29vcmRzKQ0KYGBge3J9DQp4Y29vcmRzKHZwdHMpDQp5Y29vcmRzKHZwdHMpDQpiYm94KHZwdHMpDQpgYGANCg0KTWV0aG9kIHByaW50IGp1Z2EgZGl3YXJpc2thbiBkYXJpIGNsYXNzIGNvb3JkcywgbmFtdW4ga2FyZW5hIGFkYSB0YW1iYWhhbiBuaWxhaSBzZWhpbmdnYSBwZXJsdSBkaWRlZmluaXNpIHVsYW5nIHNlYmFnYWkgYmVyaWt1dC4NCmBgYHtyfQ0KcHJpbnQudmNvb3JkcyA8LSBmdW5jdGlvbihvYmopIHsNCiAgcHJpbnQocGFzdGUoIigiLA0KICAgICAgICAgICAgICBmb3JtYXQoeGNvb3JkcyhvYmopKSwNCiAgICAgICAgICAgICAgIiwgIiwNCiAgICAgICAgICAgICAgZm9ybWF0KHljb29yZHMob2JqKSksDQogICAgICAgICAgICAgICI6ICIsIGZvcm1hdChuaWxhaShvYmopKSwNCiAgICAgICAgICAgICAgIikiLCBzZXA9IiIpLA0KICAgICAgICAgICBxdW90ZT1GQUxTRSkNCiAgIH0NCg0KdnB0cw0KYGBgDQoNCkJlZ2l0dSBwdWxhIGRlbmdhbiBtZXRob2QgcGxvdCBwZXJsdSBkaWRlZmluaXNpIHVsYW5nIGthcmVuYSBhZGEgdGFtYmFoYW4gbmlsYWkgc2VwZXJ0aSBiZXJpa3V0Lg0KYGBge3J9DQpwbG90LnZjb29yZHMgPC0gZnVuY3Rpb24ob2JqLCB0eHQ9RkFMU0UsIGJib3g9RkFMU0UsLi4uKSB7DQogIGlmIChiYm94KSB7DQogICAgaWYgKCF0eHQpIHsNCiAgICAgIHBsb3QoeGNvb3JkcyhvYmopLHljb29yZHMob2JqKSwuLi4pOw0KICAgIH0gZWxzZSB7DQogICAgICBwbG90KHhjb29yZHMob2JqKSx5Y29vcmRzKG9iaiksdHlwZT0ibiIsLi4uKTsNCiAgICAgIHRleHQoeGNvb3JkcyhvYmopLHljb29yZHMob2JqKSxuaWxhaShvYmopLC4uLik7DQogICAgfQ0KICAgIHggPC0gYyhiYm94KHB0cylbMV0sYmJveChwdHMpWzJdLGJib3gocHRzKVsyXSxiYm94KHB0cylbMV0pOw0KICAgIHkgPC0gYyhiYm94KHB0cylbM10sYmJveChwdHMpWzNdLGJib3gocHRzKVs0XSxiYm94KHB0cylbNF0pOw0KICAgIHBvbHlnb24oeCx5KQ0KICB9IGVsc2Ugew0KICAgIGlmICghdHh0KSB7DQogICAgICBwbG90KHhjb29yZHMob2JqKSx5Y29vcmRzKG9iaiksLi4uKTsNCiAgICB9IGVsc2Ugew0KICAgICAgcGxvdCh4Y29vcmRzKG9iaikseWNvb3JkcyhvYmopLHR5cGU9Im4iLC4uLik7DQogICAgICB0ZXh0KHhjb29yZHMob2JqKSx5Y29vcmRzKG9iaiksbmlsYWkob2JqKSwuLi4pOw0KICAgIH0NCiAgfQ0KfQ0KcGxvdCh2cHRzKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdCh2cHRzLHR4dD1ULGJib3g9VCxjb2w9ImJsdWUiKQ0KYGBgDQoNCiMgVHJhbnNmb3JtYXNpIGRlbmdhbiBNYXRlbWF0aWsNCk9iamVrIHZjb29yZHMgYmVyaXNpIHNsb3QgbnVtZXJpayAobmlsYWksIGJ1a2FuIHRpdGlrIGtvb3JkaW5hdCkgeWFuZyBkYXBhdCBkaXViYWggZGVuZ2FuIHRyYW5zZm9ybWFzaSBtYXRlbWF0aWthLg0KDQojIyBGdW5nc2kgTWF0ZW1hdGlrDQoqRnVuZ3NpIENvc2ludXMqDQpgYGB7cn0NCmNvcy52Y29vcmRzIDwtIGZ1bmN0aW9uKG9iail7DQogIHZjb29yZHMoeGNvb3JkcyhvYmopLA0KICAgICAgICAgIHljb29yZHMob2JqKSwNCiAgICAgICAgICBjb3MobmlsYWkob2JqKSkpDQp9DQpjb3MudmNvb3Jkcyh2cHRzKQ0KYGBgDQoNCipGdW5nc2kgU2ludXMqDQpgYGB7cn0NCnNpbi52Y29vcmRzIDwtIGZ1bmN0aW9uKG9iail7DQogIHZjb29yZHMoeGNvb3JkcyhvYmopLA0KICAgICAgICAgIHljb29yZHMob2JqKSwNCiAgICAgICAgICBzaW4obmlsYWkob2JqKSkpDQp9DQpzaW4udmNvb3Jkcyh2cHRzKQ0KYGBgDQoNCiMjIE1ldGhvZCBHcm91cDogTWF0aA0KQ2xhc3MgdmNvb3JkcyBtZW11bmdraW5rYW4gdW50dWsgZGlsYWt1a2FuIG9wZXJhc2kgYXJpdG1hdGlrYSB0ZXJoYWRhcCBuaWxhaW55YS4gTWVuZ2d1bmFrYW4gZnVuZ3NpIGdldCguR2VuZXJpYykgdW50dWsgbWVuZ2FtYmlsIGZ1bmdzaSBkZW5nYW4gbmFtYSBNYXRoLnZjb29yZHMgeWFuZyBkaWphbGFua2FuIHNlcGVydGkgYmVyaWt1dC4NCmBgYHtyfQ0KTWF0aC52Y29vcmRzIDwtIGZ1bmN0aW9uKG9iaikgew0KICB2Y29vcmRzKHhjb29yZHMob2JqKSwNCiAgICAgICAgICB5Y29vcmRzKG9iaiksDQogICAgICAgICAgZ2V0KC5HZW5lcmljKShuaWxhaShvYmopKSkNCn0NCnNxcnQodnB0cykNCmxvZyh2cHRzKQ0KZXhwKHZwdHMpDQpgYGANCg0KIyBPcGVyYXNpIEFyaXRtZXRpa2EgTWV0aG9kIEdyb3VwcyBPcHMNCk1ldGhvZCBHcm91cCBPcHMgbWVydXBha2FuIGZ1bmdzaSBnZW5lcmlrIHlhbmcgZGlndW5ha2FuIHVudHVrIG1lbmRlZmluaXNpa2FuIHNlbXVhIG9wZXJhc2kgYmluZXIgKG9wZXJhc2kgYW50YXIgMiBvcGVyYW5kKSBkZW5nYW4gb3BlcmF0b3Igc2ViYWdhaSBiZXJpa3V0Og0KDQotICAgYCIrIiwgIi0iLCAiKiIsICIvIiwgIl4iLCAiJSUiLCAiJS8lImANCi0gICBgIiYiLCAifCIsICIhImANCi0gICBgIj09IiwgIiE9IiwgIjwiLCAiPD0iLCAiPj0iLCAiPiJgDQoNClN1cGF5YSBiZXJqYWxhbiBkZW5nYW4gdGVwYXQgZGlwZXJsdWthbiBsb2thc2kgZGFyaSB2Y29vcmRzIHlhbmcgZGlvcGVyYXNpa2FuIGFkYWxhaCBpZGVudGlrDQpgYGB7cn0NCnNhbWVsb2MgPC0gZnVuY3Rpb24oZTEsZTIpIHsNCiAgKGxlbmd0aChuaWxhaShlMSkpPT1sZW5ndGgobmlsYWkoZTIpKQ0KICAgfHwgYWxsKHhjb29yZHMoZTEpPT14Y29vcmRzKGUyKSkNCiAgIHx8IGFsbCh5Y29vcmRzKGUxKT09eWNvb3JkcyhlMikpDQogICApDQp9DQpPcHMudmNvb3JkcyA8LSBmdW5jdGlvbihlMSxlMikgew0KICBpZiAoIXNhbWVsb2MoZTEsZTIpKQ0KICAgIHN0b3AgKCJMb2thc2kgYmVyYmVkYSIpDQogIGVsc2UgdmNvb3Jkcyh4Y29vcmRzKGUxKSwNCiAgICAgICAgICAgICAgIHljb29yZHMoZTEpLA0KICAgICAgICAgICAgICAgZ2V0KC5HZW5lcmljKSAobmlsYWkoZTEpLG5pbGFpKGUyKSkpDQp9DQp2cHRzDQp2cHRzK3ZwdHMNCnZwdHMqdnB0cw0KYGBgDQoNCiMgTWV0aG9kIFN1YnNldA0KTWV0aG9kIHN1YnNldCBkYXBhdCBkaWFrc2VzIHNlc3VhaSBkZW5nYW4ga2VidXR1aGFuLCBtaXNhbCBrb25kaXNpIHlhbmcgZGlpbmdpbmthbjoNCmB2cHRzW3hjb29yZHModnB0cykgPCAwICYgeWNvb3Jkcyh2cHRzKSA8IDBdYA0KRGFwYXQgZGl0YW5nYW5pIGRlbmdhbiBtZW5kZWZpbmlzaWthbiBtZXRob2QgYmVyaWt1dC4NCmBgYHtyfQ0KYFsudmNvb3Jkc2AgPC0gZnVuY3Rpb24oeCwgaSkgew0KICB2Y29vcmRzKHhjb29yZHMoeClbaV0sIHljb29yZHMoeClbaV0sDQogICAgICAgICAgbmlsYWkoeClbaV0pDQp9DQp2cHRzDQp2cHRzWzE6M10NCnZwdHNbeGNvb3Jkcyh2cHRzKSA8IDAgJiB5Y29vcmRzKHZwdHMpIDwgMF0NCmBgYA0KDQojIFBlbWVyaWtzYWFuIFN1YXR1IENsYXNzIE9iamVrDQp1bnR1ayBtZW5nZWNlayBhcGFrYWggc3VhdHUgb2JqZWsgbWVydXBha2FuIHN1YXR1IGNsYXNzIGRpZ3VuYWthbiBmdW5nc2kgaW5oZXJpdHMgc2VwZXJ0aSBiZXJpa3V0Lg0KYGBge3J9DQppbmhlcml0cyhwdHMsImNvb3JkcyIpDQppbmhlcml0cyhwdHMsInZjb29yZHMiKQ0KaW5oZXJpdHModnB0cywiY29vcmRzIikNCmluaGVyaXRzKHZwdHMsInZjb29yZHMiKQ0KYGBgDQp0ZXJsaWhhdCBiYWh3YSBvYmplayBwdHMgdGlkYWsgdGVybWFzdWsga2UgZGFsYW0gY2xhc3MgdmNvb3Jkcy4gU2VkYW5na2FuIG9iamVrIHZwdHMgdGVybWFzdWsga2UgZGFsYW0gY2xhc3MgY29vcmRzLCBrYXJlbmEgdnB0cyBtZXJ1cGFrYW4gdHVydW5hbiBkYXJpIHB0cyBzZWhpbmdnYSBtZW5kYXBhdCB3YXJpc2FuIChpbmhlcml0YW5jZSkgY2xhc3MgZGFyaSBvYmplayBwdHMuDQoNCiMgUGVudXR1cCBDbGFzcyBTeXN0ZW0gUzMNCkNsYXNzIHN5c3RlbSBTMyBtZW1iZXJpa2FuIGZhc2lsaXRhcyBvYmplY3Qtb3JpZW50ZWQgeWFuZyB0ZXJsYWx1IGxvbmdnYXIgc2VoaW5nZ2EgYmFueWFrIHRlY2huaWNhbCBpc3N1ZSBkYWxhbSBzaXN0ZW0gY2xhc3MgaW5pLiBTZWJhZ2FpIGNvbnRvaCBla3NwcmVzaSBiZXJpa3V0IGRpcGVyYm9sZWhrYW4gZGFsYW0gUg0KYGBge3J9DQptb2RlbCA8LSAxOjEwDQpjbGFzcyhtb2RlbCkgPC0gImxtIg0KY2xhc3MobW9kZWwpDQpgYGANCg0KcGFkYWhhbCBjbGFzcyAibG0iIG1lcnVwYWthbiBjbGFzcyB1bnR1ayBwZW1vZGVsYW4gbGluZWFyLg0K