Email             :
RPubs            : https://rpubs.com/vanessasupit/
Github           : https://github.com/vanessasupit/
Jurusan          : Statistika
Address         : ARA Center, Matana University Tower
                         Jl. CBD Barat Kav, RT.1, Curug Sangereng, Kelapa Dua, Tangerang, Banten 15810.


1 SOAL

Sebuah perusahaan memproduksi dua model kursi: A dan B. Model A membutuhkan 4 balok, 1 dudukan kursi, dan 1 sandaran kursi. Sedangkan model B membutuhkan 3 balok dan 1 dudukan kursi. Perusahaan memiliki stok awal 200 balok, 500 dudukan kursi, dan 100 sandaran kursi. Jika perusahaan membutuhkan lebih banyak balok, dudukan kursi, dan sandaran kursi, mereka dapat membeli kayu balok standar, yang harganya 80 euro per balok. Perusahaan dapat memproduksi 20 balok, 10 dudukan kursi, dan 2 sandaran kursi dari balok kayu standar. Biaya produksi model A adalah 30 euro/kursi, sedangkan biaya model B adalah 40 euro/kursi. Jika perusahaan menginformasikan bahwa jumlah minimum kursi yang harus diproduksi adalah 1000 unit per bulan. Tentukan model program linier, yang dapat meminimalkan total biaya.

Selesaiakan permasalahan optimasi diatas dengan cara:

  • Grafik
  • Manual
  • Menggunakan R
  • Menggunakan Python

2 Solutions

Kita konversi soal sebelumnya dalam bentuk tabel terlebih dahulu.

Keterangan :

  • x = Model A
  • y = Model B
  • z = Balok
Balok dudukan kursi sandaran Biaya
x 4 1 1 30
y 3 1 40
z 20 10 2 80
Ketersediaan 200 500 100

Fungsi Tujuan :

Dengan memperhatikan biaya produksi model A adalah 30 euro/kursi, biaya model B adalah 40 euro/kursi, dan biaya balok 80 euro/kursi

\[ Min(30x_x+40x_y+80x_z) \]

Fungsi Batasan :

  • Untuk produksi balok perusahaan memiliki stok awal 200 dan dapat memproduksi 20 balok lagi

\[ 4x_x+3x_y \le 200+20x_z\\ 4x_x+3x_y-20x_z \le 200 \]

  • Untuk produksi dudukan kursi perusahaan memiliki stok awal 500 dan dapat memproduksi 10 dudukan lagi

\[ x_x+x_y \le 500+10x_z\\ x_x+x_y-10x_z \le 500 \]

  • Untuk produksi sandaran kursi perusahaan memiliki stok awal 100 dan dapat memproduksi 2 sandaran lagi

\[ x_x \le 100 +2x_z \\ x_x -2x_z \le 100 \]

  • jumlah minimum model a dan b yang diproduksi

\[ x_x+x_y \ge 1000 \]

2.1 Grafis

x y z arah kendala Ketersediaan
1 1 -10 <= 500
4 3 -20 <= 200
1 0 -2 <= 100
1 1 0 => 1000

\[ 4x_x+3x_y-20x_z \le 200\\ x_x+x_y-10x_z \le 500\\ x_x -2x_z \le 100\\ x_x+x_y \ge 1000\\ x_x \ge 0\\ x_y \ge o\\ x_z \ge 0 \]

Grafiknya dapat dicek disini

2.2 Manual

Kita dapat menggunakan metode simplex untuk mengerjakan solusi manualnya.

Langkah-langkah :

  • Mengubah semua kendala ke Bentuk Kanonik Simpleks (yang semula menggunakan tanda pertidaksamaanmen jadi persamaan) dengan menambah perubah (variabel) SlackS. Perubah-perubah slack yang ada dimasukkan(ditambahkan) kefungsi sasaran dan diberikoefisien 0.
  • Tabel yang kita miliki sudah minimum jika semua \(Z_j-C_j \le 0\)
  • Jika ada \(Z_j-C_j > 0\) (positif) maka dibuat tabel baru dengan cara sebagai berikut :
    • Menentukan kolom kunci yaitu memilih nilai \(Z_j- C_j\) yang terbesar. Sebut dengan \(Z_k-C_k\) maka kolom ke-k disebut kolom kunci.
    • Pada kolom ke-k dilakukan pemeriksaan terhadap nilai \(a_{ik}\).
  • Menentukan baris kunci, yaitu nilai Ri yang terkecil, selanjutnya baris yg memuat Ri terkecil disebut baris kunci.
  • Kemudian disusun tabel baru sebagai berikut (dimulai dari baris kunci baru):
    • elemen baris kunci baru = elemen baris kunci lama dibagi \(a_{ik}\)
    • elemen baris baru = elemen baris lama - (\(a_{ik}\) x elemen baris r baru)
  • Kemudian tentukan lagi nilai Xi, Ci, Zj , Zj - Cj

Keterangan : * Baris Cj diisi dengan para koefisien Fungsi Tujuan (sasaran) * Baris Xj diisi dengan nama-nama perubah (variabel) yang ada. * Kolom B diisi dengan nama-nama perubah yang menjadi basis (variabel yang menyusun matriks Identitas) . * Kolom Cb diisi dengan para koefisien perubah yang menjadi basis * Kolom Xb diisi dengan para konstanta fungsi kendala (Nilai Sebelah Kanan/NSK). * Baris Zj diisi dengan rumus:\(\sum_{i=1}^m C_ba_{ij}\), j=1,…,n. * Kolom Ri diisi dengan rumus \(Ri = Xb/a_{ik}\) (aik = elemen elemen yang berada dalam kolom kunci, dan Ri dihitung hanya untuk aik ≥ 0)

\[ Z_{min}=30x_x+40x_y+80x_z+0S_1+0S_2+0s_3+0s_4+MV\\ 4x_x+3x_y-20x_z + S_1= 200\\ x_x+x_y-10x_z +S_2= 500\\ x_x -2x_z+S_3= 100\\ x_x+x_y -S_4+V= 1000 \] \[ Z= 48666.67 \\ x = 422\\ y = 578\\ z = 161 \]

Kita dapat nilai minimum costnya yaitu 48666.67 dengan memproduksi model A 422, model B 578 dan balok 161.

2.3 R

2.3.1 lpsolve

require(lpSolve)
## Loading required package: lpSolve
C <- c(30, 40, 80)

A <- matrix(c(1, 1, -10,
              4, 3, -20,
              1, 0, -2,
              1, 1, 0), nrow=4, byrow=TRUE)


B <- c(500, 200, 100, 1000)


constranints_direction  <- c("<=", "<=", "<=", ">=")

optimum <-  lp(direction="min",
               objective.in = C,
               const.mat = A,
               const.dir = constranints_direction,
               const.rhs = B,
               all.int = T)
# Print status: 0 = success, 2 = no feasible solution
print(optimum$status)
## [1] 0
# menunjukkan nilai X_x, X_y dan X_z
best_sol <- optimum$solution
names(best_sol) <- c("x_x", "x_y", "x_z") 
print(best_sol)
## x_x x_y x_z 
## 420 580 161

yang diproduksi oleh perusahaan yaitu : * Model A = 420 * Model B = 580 * Balok = 161

# Cek nilai biaya pada solusi optimal
print(paste("Total Biaya minimum: ", optimum$objval, sep=""))
## [1] "Total Biaya minimum: 48680"
rm(optimum, constranints_direction, best_sol)

2.3.2 lpSolveAPI

require(lpSolveAPI)
## Loading required package: lpSolveAPI
lprec <- make.lp(nrow = 4, ncol = 3)

lp.control(lprec, sense="min")
## $anti.degen
## [1] "fixedvars" "stalling" 
## 
## $basis.crash
## [1] "none"
## 
## $bb.depthlimit
## [1] -50
## 
## $bb.floorfirst
## [1] "automatic"
## 
## $bb.rule
## [1] "pseudononint" "greedy"       "dynamic"      "rcostfixing" 
## 
## $break.at.first
## [1] FALSE
## 
## $break.at.value
## [1] -1e+30
## 
## $epsilon
##       epsb       epsd      epsel     epsint epsperturb   epspivot 
##      1e-10      1e-09      1e-12      1e-07      1e-05      2e-07 
## 
## $improve
## [1] "dualfeas" "thetagap"
## 
## $infinite
## [1] 1e+30
## 
## $maxpivot
## [1] 250
## 
## $mip.gap
## absolute relative 
##    1e-11    1e-11 
## 
## $negrange
## [1] -1e+06
## 
## $obj.in.basis
## [1] TRUE
## 
## $pivoting
## [1] "devex"    "adaptive"
## 
## $presolve
## [1] "none"
## 
## $scalelimit
## [1] 5
## 
## $scaling
## [1] "geometric"   "equilibrate" "integers"   
## 
## $sense
## [1] "minimize"
## 
## $simplextype
## [1] "dual"   "primal"
## 
## $timeout
## [1] 0
## 
## $verbose
## [1] "neutral"
set.type(lprec, 1:3, type=c("integer"))

set.objfn(lprec, C)

# Tambahkan kendala
add.constraint(lprec, A[1, ], "<=", B[1])
add.constraint(lprec, A[2, ], "<=", B[2])
add.constraint(lprec, A[3, ], "<=", B[3])
add.constraint(lprec, A[4, ], ">=", B[4])
lprec
## Model name: 
##            C1   C2   C3            
## Minimize   30   40   80            
## R1          0    0    0  free     0
## R2          0    0    0  free     0
## R3          0    0    0  free     0
## R4          0    0    0  free     0
## R5          1    1  -10    <=   500
## R6          4    3  -20    <=   200
## R7          1    0   -2    <=   100
## R8          1    1    0    >=  1000
## Kind      Std  Std  Std            
## Type      Int  Int  Int            
## Upper     Inf  Inf  Inf            
## Lower       0    0    0
solve(lprec)
## [1] 0

Nilai \(x_x, x_y , x_z\)

get.variables(lprec)
## [1] 420 580 161

Nilai fungsi tujuan

get.objective(lprec)
## [1] 48680

2.4 Python

Pengerjaan python saya lakukan di colabs

LS0tDQp0aXRsZTogIlR1Z2FzIE9wdGltYXNpICINCnN1YnRpdGxlOiAiV2VlayA0Ig0KYXV0aG9yOiAiVmFuZXNzYSBTdXBpdCAoMjAxOTQ5MjAwMTQpIg0KZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpLCAnJUIgJWQsICVZJylgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDogDQogICAgaHRtbF9kb2N1bWVudDogbnVsbA0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRoZW1lOiBzYW5kc3RvbmUNCiAgICBjc3M6IHN0eWxlMS5jc3MNCiAgICBoaWdobGlnaHQ6IG1vbm9jaHJvbWUNCi0tLQ0KDQpgYGB7ciBsb2dvLCBlY2hvPUZBTFNFLGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoID0gJzMwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygibG9nby5wbmciKQ0KYGBgDQoNCkVtYWlsICZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyZuYnNwOzogIGRzY2llbmNlbGFic0BvdXRsb29rLmNvbSA8YnI+DQpSUHVicyAgJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7OiBodHRwczovL3JwdWJzLmNvbS92YW5lc3Nhc3VwaXQvIDxicj4NCkdpdGh1YiAgJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7ICZuYnNwOyAmbmJzcDsgOiBodHRwczovL2dpdGh1Yi5jb20vdmFuZXNzYXN1cGl0LyA8YnI+DQpKdXJ1c2FuICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDs6IFtTdGF0aXN0aWthXShodHRwczovL21hdGFuYXVuaXZlcnNpdHkuYWMuaWQvP2x5PWFjYWRlbWljJmM9c2IpIDxicj4NCkFkZHJlc3MgICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyA6IEFSQSBDZW50ZXIsIE1hdGFuYSBVbml2ZXJzaXR5IFRvd2VyIDxicj4NCiZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7ICZuYnNwOyAmbmJzcDsgJm5ic3A7Jm5ic3A7IEpsLiBDQkQgQmFyYXQgS2F2LCBSVC4xLCBDdXJ1ZyBTYW5nZXJlbmcsIEtlbGFwYSBEdWEsIFRhbmdlcmFuZywgQmFudGVuIDE1ODEwLg0KDQoqKioqDQojIFNPQUwNCg0KU2VidWFoIHBlcnVzYWhhYW4gbWVtcHJvZHVrc2kgZHVhIG1vZGVsIGt1cnNpOiBBIGRhbiBCLiBNb2RlbCBBIG1lbWJ1dHVoa2FuIDQgYmFsb2ssIDEgZHVkdWthbiBrdXJzaSwgZGFuIDEgc2FuZGFyYW4ga3Vyc2kuIFNlZGFuZ2thbiBtb2RlbCBCIG1lbWJ1dHVoa2FuIDMgYmFsb2sgZGFuIDEgZHVkdWthbiBrdXJzaS4gUGVydXNhaGFhbiBtZW1pbGlraSBzdG9rIGF3YWwgMjAwIGJhbG9rLCA1MDAgZHVkdWthbiBrdXJzaSwgZGFuIDEwMCBzYW5kYXJhbiBrdXJzaS4gSmlrYSBwZXJ1c2FoYWFuIG1lbWJ1dHVoa2FuIGxlYmloIGJhbnlhayBiYWxvaywgZHVkdWthbiBrdXJzaSwgZGFuIHNhbmRhcmFuIGt1cnNpLCBtZXJla2EgZGFwYXQgbWVtYmVsaSBrYXl1IGJhbG9rIHN0YW5kYXIsIHlhbmcgaGFyZ2FueWEgODAgZXVybyBwZXIgYmFsb2suIFBlcnVzYWhhYW4gZGFwYXQgbWVtcHJvZHVrc2kgMjAgYmFsb2ssIDEwIGR1ZHVrYW4ga3Vyc2ksICBkYW4gMiBzYW5kYXJhbiBrdXJzaSBkYXJpIGJhbG9rIGtheXUgc3RhbmRhci4gQmlheWEgcHJvZHVrc2kgbW9kZWwgQSBhZGFsYWggMzAgZXVyby9rdXJzaSwgc2VkYW5na2FuIGJpYXlhIG1vZGVsIEIgYWRhbGFoIDQwIGV1cm8va3Vyc2kuIEppa2EgcGVydXNhaGFhbiBtZW5naW5mb3JtYXNpa2FuIGJhaHdhIGp1bWxhaCBtaW5pbXVtIGt1cnNpIHlhbmcgaGFydXMgZGlwcm9kdWtzaSBhZGFsYWggMTAwMCB1bml0IHBlciBidWxhbi4gVGVudHVrYW4gbW9kZWwgcHJvZ3JhbSBsaW5pZXIsIHlhbmcgZGFwYXQgbWVtaW5pbWFsa2FuIHRvdGFsIGJpYXlhLg0KDQpTZWxlc2FpYWthbiBwZXJtYXNhbGFoYW4gb3B0aW1hc2kgZGlhdGFzIGRlbmdhbiBjYXJhOg0KDQoqIEdyYWZpaw0KKiBNYW51YWwNCiogTWVuZ2d1bmFrYW4gUg0KKiBNZW5nZ3VuYWthbiBQeXRob24NCg0KIyBTb2x1dGlvbnMNCg0KS2l0YSBrb252ZXJzaSBzb2FsIHNlYmVsdW1ueWEgZGFsYW0gYmVudHVrIHRhYmVsIHRlcmxlYmloIGRhaHVsdS4NCg0KS2V0ZXJhbmdhbiA6DQoNCiogeCA9IE1vZGVsIEENCiogeSA9IE1vZGVsIEINCiogeiA9IEJhbG9rDQoNCg0KKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSsNCnwgICAgICAgICAgICAgfCBCYWxvayAgICB8ZHVkdWthbiBrdXJzaXxzYW5kYXJhbiAgfEJpYXlhICAgICAgICB8DQorPT09PT09PT09PT09PSs9PT09PT09PT09Kz09PT09PT09PT09PT0rPT09PT09PT09PSs9PT09PT09PT09PT09Kw0KfCB4ICAgICAgICAgICB8NCAgICAgICAgIHwgMSAgICAgICAgICAgfDEgICAgICAgICB8MzAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rDQp8IHkgICAgICAgICAgIHwzICAgICAgICAgfDEgICAgICAgICAgICB8ICAgICAgICAgIHw0MCAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSsNCnwgeiAgICAgICAgICAgfDIwICAgICAgICB8MTAgICAgICAgICAgIHwyICAgICAgICAgfDgwICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tKw0KfEtldGVyc2VkaWFhbiB8MjAwICAgICAgIHw1MDAgICAgICAgICAgfDEwMCAgICAgICB8ICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0rDQoNCg0KKipGdW5nc2kgVHVqdWFuKiogOg0KDQpEZW5nYW4gbWVtcGVyaGF0aWthbiBiaWF5YSBwcm9kdWtzaSBtb2RlbCBBIGFkYWxhaCAzMCBldXJvL2t1cnNpLCBiaWF5YSBtb2RlbCBCIGFkYWxhaCA0MCBldXJvL2t1cnNpLCBkYW4gYmlheWEgYmFsb2sgODAgZXVyby9rdXJzaQ0KDQokJA0KTWluKDMweF94KzQweF95KzgweF96KQ0KJCQNCg0KKipGdW5nc2kgQmF0YXNhbioqIDoNCg0KKiBVbnR1ayBwcm9kdWtzaSBiYWxvayBwZXJ1c2FoYWFuIG1lbWlsaWtpIHN0b2sgYXdhbCAyMDAgZGFuIGRhcGF0IG1lbXByb2R1a3NpIDIwIGJhbG9rIGxhZ2kNCg0KJCQgDQo0eF94KzN4X3kgXGxlIDIwMCsyMHhfelxcDQo0eF94KzN4X3ktMjB4X3ogXGxlIDIwMA0KJCQNCg0KKiBVbnR1ayBwcm9kdWtzaSBkdWR1a2FuIGt1cnNpIHBlcnVzYWhhYW4gbWVtaWxpa2kgc3RvayBhd2FsIDUwMCBkYW4gZGFwYXQgbWVtcHJvZHVrc2kgMTAgZHVkdWthbiBsYWdpIA0KDQokJA0KeF94K3hfeSBcbGUgNTAwKzEweF96XFwNCnhfeCt4X3ktMTB4X3ogXGxlIDUwMA0KJCQNCg0KKiBVbnR1ayBwcm9kdWtzaSBzYW5kYXJhbiBrdXJzaSBwZXJ1c2FoYWFuIG1lbWlsaWtpIHN0b2sgYXdhbCAxMDAgZGFuIGRhcGF0IG1lbXByb2R1a3NpIDIgc2FuZGFyYW4gbGFnaSANCg0KJCQNCnhfeCBcbGUgMTAwICsyeF96IFxcDQp4X3ggLTJ4X3ogXGxlIDEwMA0KJCQNCg0KKiBqdW1sYWggbWluaW11bSBtb2RlbCBhIGRhbiBiIHlhbmcgZGlwcm9kdWtzaQ0KDQokJA0KeF94K3hfeSBcZ2UgMTAwMA0KJCQNCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkocmV0aWN1bGF0ZSkNCmxpYnJhcnkoUmNwcCkNCnVzZV9jb25kYWVudigicHkzOCIsIHJlcXVpcmVkID0gVFJVRSkNCmBgYA0KDQojIyBHcmFmaXMNCg0KKy0tLS0tKy0tLS0tLSstLS0tKy0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rDQp8eCAgICB8IHkgICAgfHogICB8YXJhaCBrZW5kYWxhfEtldGVyc2VkaWFhbiAgIHwNCis9PT09PSs9PT09PT0rPT09PSs9PT09PT09PT09PT0rPT09PT09PT09PT09PT09Kw0KfDEgICAgfDEgICAgIHwtMTAgfDw9ICAgICAgICAgIHw1MDAgICAgICAgICAgICB8DQorLS0tLS0rLS0tLS0tKy0tLS0rLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSsNCnw0ICAgIHwzICAgICB8LTIwIHw8PSAgICAgICAgICB8MjAwICAgICAgICAgICAgfA0KKy0tLS0tKy0tLS0tLSstLS0tKy0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rDQp8MSAgICB8MCAgICAgfC0yICB8PD0gICAgICAgICAgfDEwMCAgICAgICAgICAgIHwNCistLS0tLSstLS0tLS0rLS0tLSstLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKw0KfDEgICAgfDEgICAgIHwwICAgfD0+ICAgICAgICAgIHwxMDAwICAgICAgICAgICB8DQorLS0tLS0rLS0tLS0tKy0tLS0rLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSsNCg0KJCQgDQo0eF94KzN4X3ktMjB4X3ogXGxlIDIwMFxcDQp4X3greF95LTEweF96IFxsZSA1MDBcXA0KeF94IC0yeF96IFxsZSAxMDBcXA0KeF94K3hfeSBcZ2UgMTAwMFxcDQp4X3ggXGdlIDBcXA0KeF95IFxnZSBvXFwNCnhfeiBcZ2UgMA0KJCQNCg0KR3JhZmlrbnlhIGRhcGF0IGRpY2VrIFtkaXNpbmldKGh0dHBzOi8vY29sYWIucmVzZWFyY2guZ29vZ2xlLmNvbS9kcml2ZS8xWTdoZDRRSE1rbi1HcVJXZkg5LTl3SXh4dVNaa2hzeWs/dXNwPXNoYXJpbmcpDQoNCiMjIE1hbnVhbA0KDQpLaXRhIGRhcGF0IG1lbmdndW5ha2FuIG1ldG9kZSBzaW1wbGV4IHVudHVrIG1lbmdlcmpha2FuIHNvbHVzaSBtYW51YWxueWEuDQoNCioqTGFuZ2thaC1sYW5na2FoKiogOg0KDQoqIE1lbmd1YmFoIHNlbXVhIGtlbmRhbGEga2UgQmVudHVrIEthbm9uaWsgU2ltcGxla3MgKHlhbmcgc2VtdWxhIG1lbmdndW5ha2FuIHRhbmRhIHBlcnRpZGFrc2FtYWFubWVuIGphZGkgcGVyc2FtYWFuKSBkZW5nYW4gbWVuYW1iYWggcGVydWJhaCAodmFyaWFiZWwpIFNsYWNrUy4gUGVydWJhaC1wZXJ1YmFoIHNsYWNrIHlhbmcgYWRhIGRpbWFzdWtrYW4oZGl0YW1iYWhrYW4pIGtlZnVuZ3NpIHNhc2FyYW4gZGFuIGRpYmVyaWtvZWZpc2llbiAwLg0KKiBUYWJlbCB5YW5nIGtpdGEgbWlsaWtpIHN1ZGFoIG1pbmltdW0gamlrYSBzZW11YSAkWl9qLUNfaiBcbGUgMCQNCiogSmlrYSBhZGEgJFpfai1DX2ogPiAwJCAocG9zaXRpZikgbWFrYSBkaWJ1YXQgdGFiZWwgYmFydSBkZW5nYW4gY2FyYSBzZWJhZ2FpIGJlcmlrdXQgOg0KICArIE1lbmVudHVrYW4ga29sb20ga3VuY2kgeWFpdHUgbWVtaWxpaCBuaWxhaSAkWl9qLSBDX2okIHlhbmcgdGVyYmVzYXIuIFNlYnV0IGRlbmdhbiAkWl9rLUNfayQgbWFrYSBrb2xvbSBrZS1rIGRpc2VidXQga29sb20ga3VuY2kuDQogICsgUGFkYSBrb2xvbSBrZS1rIGRpbGFrdWthbiBwZW1lcmlrc2FhbiB0ZXJoYWRhcCBuaWxhaSAkYV97aWt9JC4NCiogTWVuZW50dWthbiBiYXJpcyBrdW5jaSwgeWFpdHUgbmlsYWkgUmkgeWFuZyB0ZXJrZWNpbCwgc2VsYW5qdXRueWEgYmFyaXMgeWcgbWVtdWF0IFJpIHRlcmtlY2lsIGRpc2VidXQgYmFyaXMga3VuY2kuDQoqIEtlbXVkaWFuIGRpc3VzdW4gdGFiZWwgYmFydSBzZWJhZ2FpIGJlcmlrdXQgKGRpbXVsYWkgZGFyaSBiYXJpcyBrdW5jaSBiYXJ1KToNCiAgKyBlbGVtZW4gYmFyaXMga3VuY2kgYmFydSA9IGVsZW1lbiBiYXJpcyBrdW5jaSBsYW1hIGRpYmFnaSAkYV97aWt9JA0KICArIGVsZW1lbiBiYXJpcyBiYXJ1ID0gZWxlbWVuIGJhcmlzIGxhbWEgLSAoJGFfe2lrfSQgeCBlbGVtZW4gYmFyaXMgciBiYXJ1KQ0KKiBLZW11ZGlhbiB0ZW50dWthbiBsYWdpIG5pbGFpIFhpLCBDaSwgWmogLCBaaiAtIENqDQoNCioqS2V0ZXJhbmdhbioqIDoNCiogQmFyaXMgQ2ogZGlpc2kgZGVuZ2FuIHBhcmEga29lZmlzaWVuIEZ1bmdzaSBUdWp1YW4gKHNhc2FyYW4pDQoqIEJhcmlzIFhqIGRpaXNpIGRlbmdhbiBuYW1hLW5hbWEgcGVydWJhaCAodmFyaWFiZWwpIHlhbmcgYWRhLg0KKiBLb2xvbSBCIGRpaXNpIGRlbmdhbiBuYW1hLW5hbWEgcGVydWJhaCB5YW5nIG1lbmphZGkgYmFzaXMgKHZhcmlhYmVsIHlhbmcgbWVueXVzdW4gbWF0cmlrcyBJZGVudGl0YXMpIC4NCiogS29sb20gQ2IgZGlpc2kgZGVuZ2FuIHBhcmEga29lZmlzaWVuIHBlcnViYWggeWFuZyBtZW5qYWRpIGJhc2lzDQoqIEtvbG9tIFhiIGRpaXNpIGRlbmdhbiBwYXJhIGtvbnN0YW50YSBmdW5nc2kga2VuZGFsYSAoTmlsYWkgU2ViZWxhaCBLYW5hbi9OU0spLg0KKiBCYXJpcyBaaiBkaWlzaSBkZW5nYW4gcnVtdXM6JFxzdW1fe2k9MX1ebSBDX2JhX3tpan0kLCBqPTEsLi4uLG4uDQoqIEtvbG9tIFJpIGRpaXNpIGRlbmdhbiBydW11cyAkUmkgPSBYYi9hX3tpa30kIChhaWsgPSBlbGVtZW4gZWxlbWVuIHlhbmcgYmVyYWRhIGRhbGFtIGtvbG9tIGt1bmNpLCBkYW4gUmkgZGloaXR1bmcgaGFueWEgdW50dWsgYWlrIOKJpSAwKQ0KDQokJA0KWl97bWlufT0zMHhfeCs0MHhfeSs4MHhfeiswU18xKzBTXzIrMHNfMyswc180K01WXFwNCjR4X3grM3hfeS0yMHhfeiArIFNfMT0gMjAwXFwNCnhfeCt4X3ktMTB4X3ogK1NfMj0gNTAwXFwNCnhfeCAtMnhfeitTXzM9IDEwMFxcDQp4X3greF95IC1TXzQrVj0gMTAwMA0KJCQNCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0nMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnVGFiZWwyLmpwZWcnKQ0KYGBgDQokJA0KWj0gIDQ4NjY2LjY3IFxcDQp4ID0gNDIyXFwNCnkgPSA1NzhcXA0KeiA9IDE2MQ0KJCQNCg0KS2l0YSBkYXBhdCBuaWxhaSBtaW5pbXVtIGNvc3RueWEgeWFpdHUgNDg2NjYuNjcgZGVuZ2FuIG1lbXByb2R1a3NpIG1vZGVsIEEgNDIyLCBtb2RlbCBCIDU3OCBkYW4gYmFsb2sgMTYxLiANCg0KIyMgUg0KDQojIyMgbHBzb2x2ZQ0KDQpgYGB7cn0NCnJlcXVpcmUobHBTb2x2ZSkNCg0KQyA8LSBjKDMwLCA0MCwgODApDQoNCkEgPC0gbWF0cml4KGMoMSwgMSwgLTEwLA0KICAgICAgICAgICAgICA0LCAzLCAtMjAsDQogICAgICAgICAgICAgIDEsIDAsIC0yLA0KICAgICAgICAgICAgICAxLCAxLCAwKSwgbnJvdz00LCBieXJvdz1UUlVFKQ0KDQoNCkIgPC0gYyg1MDAsIDIwMCwgMTAwLCAxMDAwKQ0KDQoNCmNvbnN0cmFuaW50c19kaXJlY3Rpb24gIDwtIGMoIjw9IiwgIjw9IiwgIjw9IiwgIj49IikNCg0Kb3B0aW11bSA8LSAgbHAoZGlyZWN0aW9uPSJtaW4iLA0KICAgICAgICAgICAgICAgb2JqZWN0aXZlLmluID0gQywNCiAgICAgICAgICAgICAgIGNvbnN0Lm1hdCA9IEEsDQogICAgICAgICAgICAgICBjb25zdC5kaXIgPSBjb25zdHJhbmludHNfZGlyZWN0aW9uLA0KICAgICAgICAgICAgICAgY29uc3QucmhzID0gQiwNCiAgICAgICAgICAgICAgIGFsbC5pbnQgPSBUKQ0KYGBgDQoNCg0KYGBge3J9DQojIFByaW50IHN0YXR1czogMCA9IHN1Y2Nlc3MsIDIgPSBubyBmZWFzaWJsZSBzb2x1dGlvbg0KcHJpbnQob3B0aW11bSRzdGF0dXMpDQpgYGANCg0KDQpgYGB7cn0NCiMgbWVudW5qdWtrYW4gbmlsYWkgWF94LCBYX3kgZGFuIFhfeg0KYmVzdF9zb2wgPC0gb3B0aW11bSRzb2x1dGlvbg0KbmFtZXMoYmVzdF9zb2wpIDwtIGMoInhfeCIsICJ4X3kiLCAieF96IikgDQpwcmludChiZXN0X3NvbCkNCmBgYA0KDQp5YW5nIGRpcHJvZHVrc2kgb2xlaCBwZXJ1c2FoYWFuIHlhaXR1IDoNCiogTW9kZWwgQSA9IDQyMA0KKiBNb2RlbCBCID0gNTgwDQoqIEJhbG9rID0gMTYxDQoNCmBgYHtyfQ0KIyBDZWsgbmlsYWkgYmlheWEgcGFkYSBzb2x1c2kgb3B0aW1hbA0KcHJpbnQocGFzdGUoIlRvdGFsIEJpYXlhIG1pbmltdW06ICIsIG9wdGltdW0kb2JqdmFsLCBzZXA9IiIpKQ0KYGBgDQoNCmBgYHtyfQ0Kcm0ob3B0aW11bSwgY29uc3RyYW5pbnRzX2RpcmVjdGlvbiwgYmVzdF9zb2wpDQpgYGANCg0KIyMjIGxwU29sdmVBUEkNCg0KYGBge3J9DQpyZXF1aXJlKGxwU29sdmVBUEkpDQoNCmxwcmVjIDwtIG1ha2UubHAobnJvdyA9IDQsIG5jb2wgPSAzKQ0KDQpscC5jb250cm9sKGxwcmVjLCBzZW5zZT0ibWluIikNCg0Kc2V0LnR5cGUobHByZWMsIDE6MywgdHlwZT1jKCJpbnRlZ2VyIikpDQoNCnNldC5vYmpmbihscHJlYywgQykNCg0KIyBUYW1iYWhrYW4ga2VuZGFsYQ0KYWRkLmNvbnN0cmFpbnQobHByZWMsIEFbMSwgXSwgIjw9IiwgQlsxXSkNCmFkZC5jb25zdHJhaW50KGxwcmVjLCBBWzIsIF0sICI8PSIsIEJbMl0pDQphZGQuY29uc3RyYWludChscHJlYywgQVszLCBdLCAiPD0iLCBCWzNdKQ0KYWRkLmNvbnN0cmFpbnQobHByZWMsIEFbNCwgXSwgIj49IiwgQls0XSkNCmBgYA0KDQpgYGB7cn0NCmxwcmVjDQpzb2x2ZShscHJlYykNCmBgYA0KDQpOaWxhaSAkeF94LCB4X3kgLCB4X3okDQpgYGB7cn0NCmdldC52YXJpYWJsZXMobHByZWMpDQpgYGANCg0KTmlsYWkgZnVuZ3NpIHR1anVhbg0KYGBge3J9DQpnZXQub2JqZWN0aXZlKGxwcmVjKQ0KYGBgDQoNCiMjIFB5dGhvbg0KDQpQZW5nZXJqYWFuIHB5dGhvbiBzYXlhIGxha3VrYW4gZGkgW2NvbGFic10oaHR0cHM6Ly9jb2xhYi5yZXNlYXJjaC5nb29nbGUuY29tL2RyaXZlLzFrekE2RzBMNW50UThvaERObXJpakpVeWg3WFM1WjNCbD91c3A9c2hhcmluZykgDQo=