Bu sunum, Uzay Çetin tarafından Sarıyer Akademi ile birlikte katıldığımız, 11-12 Kasım, 2017 tarihlerinde MEF Üniversitesi’nde gerçekleştirilen “Eğitimde Gelecek Konferansı”nda kullanılmak üzere hazırlanmıştır.

Bilgisayımı Gerçekten Öğrenmek İstiyor musunuz?

Bilgisayımın temelleri kodlama ve matematiktir.

Kodlama ve matematik bilgisi olmadan yapay zekayı kullanmamız, bu bilim dalına yön vermemiz mümkün değildir. Liseden itibaren öğrencilerimize bu bakış açısını kazandırmamız gerekmektedir. İki bölümden oluşacak olan sunumda, ilk bölümde kodlama ve simülasyonun önemi dinleyicilere aktarılacak. İkinci bölümde ise kalkülüs, doğrusal cebir ve istatistik bilgisinin nasıl yapay zekanın temellerini oluşturduğundan bahsedilecektir. Bu sunumun hedefi, bilim yapmanın üçüncü yolu olarak nitelendirilen simülasyonun ve bilimlerin anası olarak kabul gören matematiğin ne işe yaradığını tekrar vurgulamaktır.

Gereken Programlar: R (RStudio) ve NetLogo dilleri sunum sırasınca kullanılacaktır. Aktif olarak katılmak isteyen dinleyicilerin bilgisayarlarında bu programlama dillerinin bulunması faydalı olacaktır.

Bilgisayar, Yapay Zeka, Karmaşık Sistemler

Bilgisayar mühendisliğinin temelinde yatan algoritmik yaklaşıma göre,

tarif edebildiğimiz her şeyi programlayabiliriz. Peki ya tarif edemediğimiz şeyler?

Karmaşık sistemler bilim dalının temellerinde yatan ana fikir,

bütün, parçaların toplamından daha fazladır” ilkesidir.

Bu parçalarda bulunmayan bir özelliğin, bütünde zuhur edebileceğini ifade eder.

Örneğin tek başına bir karınca zeki değildir, ama bir bütün halinde koloni en kısa yol problemini çözerbilecek zeka belirtisi gösterir.

Yapay öğrenmenin başarısının arkasında ise “bol miktardaki örneklerden hatayı minimize eden hipotezi bulmak” yatar.

Bu sayede, elimizdeki örnek veriler ve tahminimiz arasındaki hatayı minimize ederek, algoritmasını yazamadığımız, tarifini bilemediğimiz fakat kolayca yapabildiğimiz yüz tanıma, karakter tanıma gibi işleri bilgisayarlara yaptırabiliriz.

Bu çalışma, yapay zeka ve karmaşık sistemler bilim dallarının bir arada ele alınması gerektiğini ve yapay zekaya dair çalışmaların geleceğinde, karmaşık sistemlerin büyük bir rol üstleneceğini iddia eder.

Evrene Bakış Açısı

Seth Lloyd 2005 yılında basılmış, Programming the Universe adlı kitabında söyle söylüyor,

Evrendeki her atom, her parçacık bilgi kaydeder. Bu parçacıklar arasındaki her bir çarpışma, ne kadar küçük olursa olsun, meydana gelen her bir değişim sistematik bir biçimde o bilginin işlenmesidir.

Buradaki iki kritik nokta, bilgi kaydı ve bilginin işlenmesidir. Bilindiği üzere bunlar modern programlamanın temel unsurlarıdır.

Demek ki, sadece bilgisayarlar bilgi depolayan ve o bilgiyi işleyen aygıtlar değildir. Evrenin kendisi de, baştan sona bilgi işleyen devasa bir sistemdir.

Venüs-sinekkapan Bitkisi

Bigisayım Örneği Venüs bitkisi, beyni olmamasına rağmen, sayım yapıyor ve şartlar sağlandığında harekete geçiyor.

Bilgi İşlem

  • Durum: Açık ya da Kapalı
  • Kural:
    • 1 kez dokunduysa hazırlan
    • 20 sn içinde 2. kez dokunduysa kapan

Meksika Dalgası

Zuhur Örneği

Parçalarda olmayan bir şey (dalga), bütünde ortaya çıkıyor.

Bilgi İşlem

  • Durum: Ayakta ya da Oturmuş
  • Kural:
    • Yakın komşu ayakta ise, ayağa kalk
    • ayakta ise, bir süre sonra otur

En Kısa Yolu Bulan Karıncalar

Zuhur Örneği

Parçalarda olmayan bir şey (zeka), bütünde ortaya çıkıyor.

Bu resim Goss ve arkadaşlarının 1989 yılındaki Naturwissemschaften makalesinden alınmıştır. (a) Deney tasarımı (b) 4 dakika sonra köprü üzerindeki karıncalar (a) 8 dakika sonra en kısa yol üzerinde yoğunlaşmış karıncalar.

Bilgi İşlem

  • Durum: Yemek-Ara ya da Yemek-Bulundu
  • Kural:
    • Yemek-Ara ise kokunun yoğun olduğu tarafa git
    • Yemek-Bulundu ise koku bırak

Ekosistem

Öz örgütlenim

Doğa değişen duruma göre kendini yeniden programlıyor.

Ekosistemi oluşturan canlılar arasındaki karmaşık bağlantılar bir çırpıda bilgi işlemi yazmayı zorlaştırıyor. Burada ilginç olan bir çok nokta var, şimdilik her şeyi diğer her şeye olan bağlantısını vurgulamakla yetinelim. Yellowstone parkına, getirilen kurtlar geyik nüfusunu azaltıyor, orman ve bitki örtüsü yeniden canlanıyor, erozyon azalıyor, nehir yatakları güçleniyor ve parkın fiziki coğrafyası değişiyor. Bir kaç kurtun bunu yapabileceğini kim tahmin ederdi?

Ekosistem

Algoritma

Algoritmalar, bilgisayar biliminin yapı taşıdır. Belirli bir problemin nasıl çözüleceğine dair izlenmesi gereken adımların tümüne algoritma deriz. Çözüm yolunu düşünürüz, ve bu düşüncemizi koda dönüştürürüz.

Algoritmalar kodun içerisine gömülmüş düşüncelerdir

Tarif edebilidiğimiz her şeyin programını yazabiliriz. Bir işi tarif edebilmek, o işin algoritmasını bilmek demektir. Örneğin, menemen yapmayı biliyorsanız, bana yemeğin algoritmasını yani tarifini verebilirsiniz.

Bilgisayar bilimindeki en önemli algoritmaların başında sıralama algoritmaları gelir. Google sizce arama motoru mudur, yoksa sıralama motoru mudur?

# Ekle-sırala algoritması
1.Döngü: i = 0'dan N'ye, her i için, # i'yi Ekle
    2.Döngü: j = i'den 1'e, her j için, 
        j.inci eleman, bir öncekiden küçükse, # i'yi Sırala
            ikisinin yerini değiştir
            j'yi bir azalt
        değilse
            2.Döngüden çık
    i'yi bir arttır

Yukarıda ekle-sırala algoritmasını aşağıda görüyorsunuz. İlk bakışta karışık gelebilir ama aslında oldukça basittir.

Şöyle düşünün, masadaki desteden kart seçiyorsunuz. İlk kartı sıralamaya gerek yok. İkinci kartı, ilkiyle karşılaştırıp küçükse sola koyarsınız. Bu şekilde, masadan aldığını yeni kartı elinize ekle rsiniz ve sonra sırala rsınız.

Aşağıdaki videoda, folklor grubu ekle-sırala algoritmasına göre dans ediyor. Bu videoya bakınca, ne demek istediğimi daha iyi anlayabilirsiniz.

Aşağıdaki videoda, karganın uyguladığı algoritmayı görebiliyor musunuz?

Algortima , yemek yapmaktan dans etmeye her şey algoritmadır. Bir işin nasıl gerçekleştirileceğini anlatan sıralı komutlar.

Hatırlatmalar

  • Bilim yapmanın 3 yolu
  • Bilgisayım , sinek-kapan’dan kara deliklere, oradan K-means algoritmasına
  • Model , önemli olanları tut, önemsiz olanları görmezden gel. Karikatür, haritalar vb..
  • Algortima , yemek yapmaktan dans etmeye her şey algoritmadır. Bir işin nasıl gerçekleştirileceğini anlatan sıralı komutlar.
  • Programlama nın temelelleri

    • Eğer .. değilse ..
    • Döngüler, tekrarlar
    • Fonksiyonlar, ismi olan küçük program parçaları

Netlogo ve Karmaşık Sistemler

Aşağıdaki komutları Netlogoda yazarsak ne olur?

Forward 50
Right 90
Forward 50
Right 90
Forward 50
Right 90
Forward 50
Right 90
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
to setup
  clear-all                  ;; her seyi sil
  create-turtles 1[
    set heading -30          ;; ilk yonu belirle
    setxy (5 - max-pxcor) 0  ;; ilk konum
  ]
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
to ciz                      ;; kaplumbagalardan rica et
  ask turtles [polygon-ciz]
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
to polygon-ciz                 ;; yeni fonksiyon (prosedur)
   pen-down                    ;; kalem cizim icin gerekli
   repeat kenar-sayisi[        ;; Dongu
    forward uzunluk            ;; Ilerle
    right (360 / kenar-sayisi) ;; Saga Don
   ]
end

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
globals [venus.x venus.y]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
to setup
  ca
  crt 5 [
    set xcor random-xcor
    set ycor random-ycor
    set shape "bug"
    set size 1.5
  ]
  set venus.x random-pxcor
  set venus.y random-pycor
  venus-flytraps 1
  reset-ticks
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
to go
  ask turtles [
    set heading towardsxy venus.x venus.y
    set heading (heading + 15 - random 30)
    wait 0.025
    fd 0.5
  ]

  let bocek.dokundu? false
  ask turtles[
    if distancexy venus.x venus.y < 0.5 [
      show "bocek dokundu"
    ]
  ]

  tick
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; venus-fltrap : Etcil Cicek Simulasvenusyonu
;;
;; >> prosedurler (fonksovenus.yonlar) kodumuzu organize etmemize venus.yardimci olurlar.
;; >>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; istedigim kadar etcil cicek yaratabiliyorum
;; parametere : tane
to venus-flytraps [tane]
  repeat tane[venus-flytrap ]
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Etcil cicekin merkez noktasi venus.x venus.y
to venus-flytrap
  venus-ac venus.x venus.y
  wait 0.125          ;; 0.125 sn bekle
  venus-kapa venus.x venus.y
  wait 0.25           ;; 0.25 sn bekle
  venus-ac venus.x venus.y
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Belli uzakliktaki parselleri (patch) venus.yesil venus.yap, merkez kirmizi
;; >> Asagidaki iki komutun sirasi degirse ne olur?
;; >> Kodun organizasyonu cok onemlidir.
to venus-ac [x y]
  ask patches with [distancexy venus.x venus.y < 2.3] [ set pcolor green ]
  ask patch x y [set pcolor red]
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Belli uzakliktaki parselleri (patch) yesil yap, merkez kirmizi olsun
to venus-kapa [x y]
  ;; yesil olan uc kisimlari, tekrar siyah yapiyoruz
  ask patches with [distancexy venus.x venus.y < 2.3] [ set pcolor black ]
  ask patches with [distancexy venus.x venus.y < 2.1] [ set pcolor green ]
  ask patches with [distancexy venus.x venus.y < 2.0] [ set pcolor orange ]
  ask patch x y [set pcolor red]
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Deneyin ne oluyor? go fonksiyonun içini aşağıdaki gibi yazın.

  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
to go
  ask turtles [
    set heading towardsxy venus.x venus.y
    set heading (heading + 15 - random 30)
    wait 0.025
    fd 0.5
  ]

  let bocek.dokundu? false
  ask turtles[
    if distancexy venus.x venus.y < 0.5 [
      venus-kapa venus.x venus.y
      set size 3
      set bocek.dokundu? true
      repeat 4[
        wait 0.1
        set size random 4
      ]
      die
    ]
  ]

  if bocek.dokundu? [
    venus-kapa venus.x venus.y
    wait 0.5           ;; 0.25 sn bekle
    venus-ac venus.x venus.y
  ]
  tick
end

Kodu biraz geliştirelim.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
breed[sinek-kapanlar sinek-kapan]
breed[sinekler sinek]
sinek-kapanlar-own[bocek.dokundu?]
sinekler-own[hiz]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
to setup
  ca
  ask patches [set pcolor black]
  create-sinekler sinek-sayisi [
    set xcor random-xcor
    set ycor random-ycor
    set shape "bug"

    set hiz 0.2 + random-float 1
    set size 1.5;;2 - 2 * (hiz - 0.2) ;; hiz buyukluk ile ters orantili olsun

    ifelse xcor < 0 [set color cyan][set color violet]
    if xcor < (- max-pxcor / 2) [set color sky]
    if xcor >  (max-pxcor / 2) [set color pink]
  ]

  create-sinek-kapanlar kapan-sayisi[
    set xcor round random-xcor
    set ycor round random-ycor
    set color red
    set bocek.dokundu? false
  ]

  ask sinek-kapanlar [
    venus-ac xcor ycor
  ]
  reset-ticks
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
to go
  ask sinekler [
    let en-yakin-kapan min-one-of sinek-kapanlar [ distance myself ]
    set heading towards en-yakin-kapan
    set heading (heading + 15 - random 30)
    fd hiz
  ]

  ask sinek-kapanlar [
    if not any? sinekler [stop]
    let en-yakin-sinek min-one-of sinekler [ distance myself ]
    ifelse distance en-yakin-sinek < 1 [
      set bocek.dokundu? true
      ask en-yakin-sinek [die]
    ][set bocek.dokundu? false]
    ifelse bocek.dokundu? [venus-kapa xcor ycor][venus-ac xcor ycor]
  ]

  if not any? sinekler [
    ask sinek-kapanlar [venus-ac xcor ycor]
    stop]
  tick
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Belli uzakliktaki parselleri (patch) venus.yesil venus.yap, merkez kirmizi
;; >> Asagidaki iki komutun sirasi degirse ne olur?
;; >> Kodun organizasyonu cok onemlidir.
to venus-ac [x y]
  ask patches with [distancexy x y < 2.3] [ set pcolor green ]
  ask patch x y [set pcolor red]
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Belli uzakliktaki parselleri (patch) yesil yap, merkez kirmizi olsun
to venus-kapa [x y]
  ;; yesil olan uc kisimlari, tekrar siyah yapiyoruz
  ask patches with [distancexy x y < 2.3] [ set pcolor black ]
  ask patches with [distancexy x y < 2.1] [ set pcolor green ]
  ask patches with [distancexy x y < 1.8] [ set pcolor orange ]
  ask patch x y [set pcolor red]
end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Basit bir etçil çiçek simülasyonunun nasıl bir karadelik simülasyonuna benzediğine ya da nasıl k-means gibi bir kümeleme algortimasına benzediğine dikkat edin.

Aşırıya kaçmamak şartıyla, belirli bir dozda hayal gücü olmadan bilim yapılamaz.

R dili ve Yapay Öğrenmeye Giriş

Programlamanın yapı taşı kontrol ifadeleridir.

b <- 0
b <- ifelse(b==0, 1, 0)
print(paste("b = ", b))
[1] "b =  1"

R dilinin yapı taşı ise vektörlerdir.

# b vektorunde hangileri bir
b <- c(1,1,0,0,0)
which (b == 1)
[1] 1 2
# matris oluturalim
satir1 <- b
satir2 <- 1 - b
matris <- rbind(satir1, satir2) # satirlari birlestir
matris
       [,1] [,2] [,3] [,4] [,5]
satir1    1    1    0    0    0
satir2    0    0    1    1    1

Otomasyon, sıkıcı işlerin tekrarıdır.

for(i in b) print(ifelse(i==0, "yazi", "tura"))
[1] "tura"
[1] "tura"
[1] "yazi"
[1] "yazi"
[1] "yazi"

Fonksiyonlar, kodunuzu organize etmenizi sağlar.

topla <- function(a,b){return (a + b)}
topla(2,4)
[1] 6

Newton Matematiği

x = seq(-1, 10, by = 0.1)  # x Degerleri
y = x^2 + 15               # y = x^2 + 15 parabolü
#
plot(x, y, type = "l", col = 1, ylim = c(0,100),
     main = expression(paste("y =",x^2, "+ 15 fonksiyonu ve x=5 noktasındaki türev")), xaxt="n")
lines(x, 10 * x - 10, col = "blue")
#
abline(v = 5, col = "gray")
segments(5, 40, 8, 40, col= 'red')
segments(8, 40, 8, 70, col= 'red')
segments(8, 70, 8, 80, col= 'red', lwd = 3)
#
segments(7, 40, 7, 60, col= 'red')
segments(7, 60, 7, 64, col= 'red', lwd = 3)
text(6.1, 73, 'Hata', col= 'red')
arrows(6.5, 73, 7.95, 74,length = 0.08, col= 'red')
arrows(6.5, 73, 6.95, 61.5,length = 0.08, col= 'red')
text(6.5, 35, expression(paste(Delta, x)), col= 'red')
text(8.5, 60, expression(paste(Delta, y)), col= 'red')
text(2, 90, expression(paste('Tegetin egimi ', Delta, x, ' kuculdukce')), col= 'blue')
text(2.08, 83, expression(paste('hata azalarak ', Delta, y, '/', Delta, x, ' yaklasir. ')), col= 'red')
axis(1, at = seq(-1, 10, by = 1), las=2)
grid()

\(y = x^2 + 15\) fonksiyonu y ler farkı bölü x ler farkı gittikçe, \(y = 5\) noktasındaki teğete benzemeye başlıyor,

\[ \frac{\triangle y}{\triangle x} = \frac{y_2 - y_1}{y_2 - y_1} \]

Türev

Herhangi bir niceliğin, belirli bir anda ne kadar hızlı büyüdüğünü ya da küçüldüğünü ölçer. \[ \frac{dx}{dt} \]

Tekrarlı fonksiyonlar

\[ x_{n+1} = f(x_n)\]

tekrarli fonksiyonunu R ile çizelim.

\[ s_{n+1} = f(s_n) = s_n + \frac{1}{2^{n+1} }\]

islem <- function(s,n){return (s +(1/2^n))}
s <- 1
print(s)
[1] 1
s <- islem(s,1)
print(s)
[1] 1.5
s <- islem(s,2)
print(s)
[1] 1.75
s <- islem(s,3)
print(s)
[1] 1.875

Sürekli tek tek yazmak yerine, bunu bir for donüsü içinde yazalım.

t = 2:10
s = 1
print(s)
[1] 1
for(i in t){
  s <- islem(s,i)
  print(s)
}
[1] 1.25
[1] 1.375
[1] 1.4375
[1] 1.46875
[1] 1.484375
[1] 1.492188
[1] 1.496094
[1] 1.498047
[1] 1.499023
son = 20
t = 1:son
s = rep(1,son)
for(i in t[-son]){
  s[i+1] <- islem(s[i],i)
}
plot(t,s,col = "red", type ='b')
grid()

\(e_{n+1} = f(e_n) = e_n + \frac{1}{(n+1)!}\)

e.islem <- function(e,n){return (e +(1/factorial(n+1)))}
son = 20
t = 1:son
e = rep(1,son)
for(i in t[-son]){
  e[i+1] <- e.islem(e[i],i)
}
plot(t,e,col = "violet", type ='b', ylim = c(0,3),
     main = "2  ile 3 arasindaki bu sayi nedir?")
grid()

Dereceli Inis

\(f(x)\) fonksiyonun minimum noktasini

\[ x_1 = x_1 - \alpha f'(x) \]

x = seq(-20,20,0.2)
f <- function(x){x^2 + 5}
f.turev <- function(x){2*x}
dereceli.inis <- function(x1 = 10, adim.uzunluk = 0.25){
  for(i in 1:20){
    x1 <- x1 - adim.uzunluk * f.turev(x1)
    print(x1)
  }
  return(x1)
}
plot(x,f(x), type = 'l')
print(dereceli.inis())
[1] 5
[1] 2.5
[1] 1.25
[1] 0.625
[1] 0.3125
[1] 0.15625
[1] 0.078125
[1] 0.0390625
[1] 0.01953125
[1] 0.009765625
[1] 0.004882812
[1] 0.002441406
[1] 0.001220703
[1] 0.0006103516
[1] 0.0003051758
[1] 0.0001525879
[1] 7.629395e-05
[1] 3.814697e-05
[1] 1.907349e-05
[1] 9.536743e-06
[1] 9.536743e-06
grid()

################################################
# Veri hazirligi : Uc tur Cicek verisi
################################################
data_df <- as.data.frame(iris)
# iki tur cicekle ilgilenelim
tur <- data_df$Species %in% c("virginica", "setosa")
data_df <- data_df[tur,]
# 1: "virginica" ve 0: "setosa" olsun
y <- ifelse(data_df$Species=="virginica", 1, 0)
# 4 boyut (ozellik) yerine 2 boyut ozellik ile ilgilenelim => cizim kolayligi 
X <- data_df[c(1,3)]
X <- as.matrix(X) # X <- as.matrix(X/max(X))
# Verimizin ilk 6 degerlerine goz atalim
head(X)
  Sepal.Length Petal.Length
1          5.1          1.4
2          4.9          1.4
3          4.7          1.3
4          4.6          1.5
5          5.0          1.4
6          5.4          1.7
head(y)
[1] 0 0 0 0 0 0
plot(X, col = y + 1)
grid()

# theta0 icin ilk kolon 1 yapildi
X = cbind(rep(1, length(y)), X)
################################################
# Siniflandirma: 
#         Cicek "virginica" mi "setosa" mi?
#         petal-length, sepal-length verisine bakarak 
#         karar verecegiz
################################################

Modelimizi yazalım

\(\sigma(z) = \frac{1}{1 + e ^{-z}}\) ve \(x_0 = 1\), \(x_1 = sepal.length\), \(x_2 = petal.length\) olmak üzere, modelimiz ya da hipotezimiz aşağıdaki gibidir,

\[ h_\theta(x) = \sigma (\theta \cdot x) = \sigma (\theta_0 x_0 + \theta_1 x_1 + \theta_2 x_2) \] Amacımız hipotezimiz \(h_\theta\) ile gerçek çıktı olan \(y\) arasındaki farkı en az indirecek \(\theta_0\), \(\theta_1\), \(\theta_2\) değerlerini bulmaktır. Diğer bir ifadeyle,

\[ Hata(\theta) = \frac{1}{2}\sum (y - h_\theta(x))^2 \]

Bunun için eğim iniş yöntemini kullanacağız. Hatanın türevini hesaplamamız gerekiyor. \[ \frac{d Hata(\theta)}{d\theta_i} = \sum - (y - h_\theta(x)) \frac{d h_\theta(x)}{d\theta_i} \]

Demek ki, zincir kuralı gereği hipotezimin türevini hesaplamamız gerekiyor.

\[\begin{equation} \begin{split} \frac{d h_\theta(x)}{d\theta_i} &=& \frac{d \sigma (\theta \cdot x)}{d\theta_i} \\ &=& \sigma (\theta \cdot x) (1 - \sigma (\theta \cdot x)) \frac{d (\theta_0 x_0 + \theta_1 x_1 + \theta_2 x_2)}{d\theta_i} \\ &=& \sigma (\theta \cdot x) (1 - \sigma (\theta \cdot x)) x_i\\ &=& h_\theta(x) (1 - h_\theta(x)) x_i\\ \end{split} \end{equation}\]

Tüm bu matematiksel denklemleri bir araya getirip, hatamızın türevini hesaplayalım

\[ \frac{d Hata(\theta)}{d\theta_i} = \sum (h_\theta(x) - y) h_\theta(x) (1 - h_\theta(x)) x_i \]

Dereceli İniş

Hata fonksiyonun minimum noktasını elde etmek için adım adım aşağıdaki gibi ilerleyeceğiz.

\[ \theta_i = \theta_i - \alpha \frac{d Hata(\theta)}{d\theta_i} \] Sigmoid fonksiyonun dünya gözüyle bir görelim.

sigmoid <- function(z){
  return (1 / (1 + exp(-z)))
}
z = -10:10
plot(z, sigmoid(z), type="b")

dogru <- function(x, th){
  return(x %*% th)
}
hipotez <- function(x, th){
  z = dogru(x,th)
  return(sigmoid(z))
}
th = c(0.1, 0.1, 0.1)
z = X %*% th
h = sigmoid(z)
hata = sum((y - h) *  (y - h))
adim = 0.1 # alfa adim uzunlugu
for(i in 1:3){
  gradyan = sum((h-y) * h * (1-h) * X[,i])
  th[i] = th[i] - adim * gradyan
}
print(hata)
[1] 25.29043
print(th)
[1] -0.461235965 -2.443539080 -0.004985982
iterasyon <- function(X = X, y = y, th, adim = 0.5){
  z = X %*% th
  h = sigmoid(z)
  hata = sum((y - h) *  (y - h))
  for(i in 1:3){
    gradyan = mean((h-y) * h * (1-h) * X[,i])
    th[i] = th[i] - adim * gradyan
  }
  return(list(th = th , hata = hata))
}
#Intial theta
th <- rep(0,ncol(X))
simulasyon = iterasyon(X, y , th)
th = simulasyon$th
print(simulasyon)
$th
[1] 0.0000000 0.0494375 0.1278125

$hata
[1] 25
#Intial theta
th <- rep(0,ncol(X))
hata <- function(th){
  z = X %*% th
  h = sigmoid(z)
  return(sum((y - h) *  (y - h)))
}
# Derive theta using gradient descent using optim function
theta_optim <- optim(par=th,fn=hata)
#set theta
theta <- theta_optim$par
#cost at optimal value of the theta
theta_optim$par
[1] -22.59136 -12.75926  27.93210
th = c(-1,-1,1)
for(i in 1:10000){
  simulasyon = iterasyon(X, y , th, adim = 5)
  th = simulasyon$th
}
print(simulasyon)
$th
[1] -1.607568 -2.174644  4.154802

$hata
[1] 0.0009260814
#th = theta_optim$par
plot(X[,2], X[,3], col = y + 1)
model = -1 * (th[1] + th[2] * X[,2]) / th[3]
lines(X[,2],model)
grid()

Sentetik Veri

Dogrusal Sınıflandırma yöntemini kullanarak, sentetik bir veriyi iki sınıfa ayıralım.

n = 50
merkez1 = 5
kume1.x1 = rnorm(n, mean = merkez1, sd=3)
kume1.x2  = rnorm(n, mean = merkez1, sd=3)
kume1.y = rep(0,n)
merkez2 = 20
kume2.x1 = rnorm(n, mean = merkez2, sd=3)
kume2.x2  = rnorm(n, mean = merkez2, sd=3)
kume2.y = rep(0,n)
plot(kume1.x1,kume1.x2,col= "red", type = "p", xlim = c(0,30), ylim = c(0,30))
lines(kume2.x1,kume2.x2,col= "green", type = "p")
grid()

VX = cbind(rep(1,2*n),
          c(kume1.x1, kume2.x1),
          c(kume1.x2, kume2.x2))
Vy = c(rep(0,n),rep(1,n))
  
th = c(-1,-1,1)
for(i in 1:10000){
  simulasyon = iterasyon(VX, Vy , th, adim = 1)
  th = simulasyon$th
}
print(simulasyon)
$th
[1] -8.4350207  0.4008360  0.3172988

$hata
[1] 0.04096217
#th = theta_optim$par
plot(VX[,2], VX[,3], col = Vy + 2, xlim = c(0,30), ylim = c(0,30))
model = -1 * (th[1] + th[2] * VX[,2]) / th[3]
lines(VX[,2],model)
grid()

Dogrusal Baglanim

Dogrusal Baglanim

El yazısı ile yazılmış sayıları tanıma

Hatalar

LS0tCnRpdGxlOiAiWWFwYXkgWmVrYXlhIEdpcmnFnzogQmlsZ2lzYXnEsW3EsW4gVGVtZWxsZXJpIgphdXRob3I6IERyLiBVemF5IMOHZXRpbgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCmBgYHtyLCBldmFsPUZBTFNFfQpCdSBzdW51bSwgVXpheSDDh2V0aW4gdGFyYWbEsW5kYW4gU2FyxLF5ZXIgQWthZGVtaSBpbGUgYmlybGlrdGUga2F0xLFsZMSxxJ/EsW3EsXosIDExLTEyIEthc8SxbSwgMjAxNyB0YXJpaGxlcmluZGUgTUVGIMOcbml2ZXJzaXRlc2nigJluZGUgZ2Vyw6dla2xlxZ90aXJpbGVuIOKAnEXEn2l0aW1kZSBHZWxlY2VrIEtvbmZlcmFuc8Sx4oCdbmRhIGt1bGxhbsSxbG1hayDDvHplcmUgaGF6xLFybGFubcSxxZ90xLFyLgpgYGAKCgojIyBCaWxnaXNhecSxbcSxIEdlcsOnZWt0ZW4gw5bEn3Jlbm1layDEsHN0aXlvciBtdXN1bnV6PwpCaWxnaXNhecSxbcSxbiB0ZW1lbGxlcmkgX19rb2RsYW1hX18gdmUgX19tYXRlbWF0aWt0aXJfXy4KPGJyPjwvYnI+Cgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iUmVkUGlsbEJsdWVQaWxsX2Z1bGwuZ2lmIiB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSI+PC9wPgoKS29kbGFtYSB2ZSBtYXRlbWF0aWsgYmlsZ2lzaSBvbG1hZGFuIHlhcGF5IHpla2F5xLEga3VsbGFubWFtxLF6LCBidSBiaWxpbSBkYWzEsW5hIHnDtm4gdmVybWVtaXogbcO8bWvDvG4gZGXEn2lsZGlyLiBMaXNlZGVuIGl0aWJhcmVuIMO2xJ9yZW5jaWxlcmltaXplIGJ1IGJha8SxxZ8gYcOnxLFzxLFuxLEga2F6YW5kxLFybWFtxLF6IGdlcmVrbWVrdGVkaXIuIMSwa2kgYsO2bMO8bWRlbiBvbHXFn2FjYWsgb2xhbiBzdW51bWRhLCBpbGsgYsO2bMO8bWRlIGtvZGxhbWEgdmUgc2ltw7xsYXN5b251biDDtm5lbWkgZGlubGV5aWNpbGVyZSBha3RhcsSxbGFjYWsuIMSwa2luY2kgYsO2bMO8bWRlIGlzZSBrYWxrw7xsw7xzLCBkb8SfcnVzYWwgY2ViaXIgdmUgaXN0YXRpc3RpayBiaWxnaXNpbmluIG5hc8SxbCB5YXBheSB6ZWthbsSxbiB0ZW1lbGxlcmluaSBvbHXFn3R1cmR1xJ91bmRhbiBiYWhzZWRpbGVjZWt0aXIuIEJ1IHN1bnVtdW4gaGVkZWZpLCBiaWxpbSB5YXBtYW7EsW4gw7zDp8O8bmPDvCB5b2x1IG9sYXJhayBuaXRlbGVuZGlyaWxlbiBzaW3DvGxhc3lvbnVuIHZlIGJpbGltbGVyaW4gYW5hc8SxIG9sYXJhayBrYWJ1bCBnw7ZyZW4gbWF0ZW1hdGnEn2luIG5lIGnFn2UgeWFyYWTEscSfxLFuxLEgdGVrcmFyIHZ1cmd1bGFtYWt0xLFyLgoKR2VyZWtlbiBQcm9ncmFtbGFyOiBSIChSU3R1ZGlvKSB2ZSBOZXRMb2dvIGRpbGxlcmkgc3VudW0gc8SxcmFzxLFuY2Ega3VsbGFuxLFsYWNha3TEsXIuIEFrdGlmIG9sYXJhayBrYXTEsWxtYWsgaXN0ZXllbiBkaW5sZXlpY2lsZXJpbiBiaWxnaXNheWFybGFyxLFuZGEgYnUgcHJvZ3JhbWxhbWEgZGlsbGVyaW5pbiBidWx1bm1hc8SxIGZheWRhbMSxIG9sYWNha3TEsXIuCgoKIyMgQmlsZ2lzYXlhciwgWWFwYXkgWmVrYSwgS2FybWHFn8SxayBTaXN0ZW1sZXIKPHAgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOyI+PGltZyBzcmM9ImlkZGEucGRmIiAgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiPjwvcD4KCgpCaWxnaXNheWFyIG3DvGhlbmRpc2xpxJ9pbmluIHRlbWVsaW5kZSB5YXRhbiBhbGdvcml0bWlrIHlha2xhxZ/EsW1hIGfDtnJlLAoKPiB0YXJpZiBlZGViaWxkacSfaW1peiBoZXIgxZ9leWkgcHJvZ3JhbWxheWFiaWxpcml6LiBQZWtpIHlhIHRhcmlmIGVkZW1lZGnEn2ltaXogxZ9leWxlcj8KCkthcm1hxZ/EsWsgc2lzdGVtbGVyIGJpbGltIGRhbMSxbsSxbiB0ZW1lbGxlcmluZGUgeWF0YW4gYW5hIGZpa2lyLCAKCj4g4oCcX19iw7x0w7xuLCBwYXLDp2FsYXLEsW4gdG9wbGFtxLFuZGFuIGRhaGEgZmF6bGFkxLFyX1/igJ0gaWxrZXNpZGlyLiAgCgpCdSBwYXLDp2FsYXJkYSBidWx1bm1heWFuIGJpciDDtnplbGxpxJ9pbiwgYsO8dMO8bmRlIHp1aHVyIGVkZWJpbGVjZcSfaW5pIGlmYWRlIGVkZXIuIAoKPiDDlnJuZcSfaW4gdGVrIGJhxZ/EsW5hIGJpciBrYXLEsW5jYSB6ZWtpIGRlxJ9pbGRpciwgYW1hIGJpciBiw7x0w7xuIGhhbGluZGUga29sb25pIGVuIGvEsXNhIHlvbCBwcm9ibGVtaW5pIMOnw7Z6ZXJiaWxlY2VrIHpla2EgYmVsaXJ0aXNpIGfDtnN0ZXJpci4gCgpZYXBheSDDtsSfcmVubWVuaW4gYmHFn2FyxLFzxLFuxLFuIGFya2FzxLFuZGEgaXNlIOKAnGJvbCBtaWt0YXJkYWtpIMO2cm5la2xlcmRlbiBoYXRhecSxIG1pbmltaXplIGVkZW4gaGlwb3RlemkgYnVsbWFr4oCdIHlhdGFyLiAKCj4gQnUgc2F5ZWRlLCBlbGltaXpkZWtpIMO2cm5layB2ZXJpbGVyIHZlIHRhaG1pbmltaXogYXJhc8SxbmRha2kgaGF0YXnEsSBtaW5pbWl6ZSBlZGVyZWssIGFsZ29yaXRtYXPEsW7EsSB5YXphbWFkxLHEn8SxbcSxeiwgdGFyaWZpbmkgYmlsZW1lZGnEn2ltaXogZmFrYXQga29sYXljYSB5YXBhYmlsZGnEn2ltaXogecO8eiB0YW7EsW1hLCBrYXJha3RlciB0YW7EsW1hIGdpYmkgacWfbGVyaSBiaWxnaXNheWFybGFyYSB5YXB0xLFyYWJpbGlyaXouIAoKQnUgw6dhbMSxxZ9tYSwgeWFwYXkgemVrYSB2ZSBrYXJtYcWfxLFrIHNpc3RlbWxlciBiaWxpbSBkYWxsYXLEsW7EsW4gYmlyIGFyYWRhIGVsZSBhbMSxbm1hc8SxIGdlcmVrdGnEn2luaSB2ZSB5YXBheSB6ZWtheWEgZGFpciDDp2FsxLHFn21hbGFyxLFuIGdlbGVjZcSfaW5kZSwga2FybWHFn8SxayBzaXN0ZW1sZXJpbiBiw7x5w7xrIGJpciByb2wgw7xzdGxlbmVjZcSfaW5pIGlkZGlhIGVkZXIuIAoKCgojIyBFdnJlbmUgQmFrxLHFnyBBw6fEsXPEsQoKU2V0aCBMbG95ZCAyMDA1IHnEsWzEsW5kYSBiYXPEsWxtxLHFnywgUHJvZ3JhbW1pbmcgdGhlIFVuaXZlcnNlIGFkbMSxIGtpdGFixLFuZGEgc8O2eWxlIHPDtnlsw7x5b3IsCgo+IEV2cmVuZGVraSBoZXIgYXRvbSwgaGVyIHBhcsOnYWPEsWsgYmlsZ2kga2F5ZGVkZXIuIEJ1IHBhcsOnYWPEsWtsYXIgYXJhc8SxbmRha2kgaGVyIGJpciDDp2FycMSxxZ9tYSwgbmUga2FkYXIga8O8w6fDvGsgb2x1cnNhIG9sc3VuLCBtZXlkYW5hIGdlbGVuIGhlciBiaXIgZGXEn2nFn2ltIHNpc3RlbWF0aWsgYmlyIGJpw6dpbWRlIG8gYmlsZ2luaW4gacWfbGVubWVzaWRpci4gCgoKQnVyYWRha2kgaWtpIGtyaXRpayBub2t0YSwgIF9iaWxnaSBrYXlkxLFfIHZlICBfYmlsZ2luaW4gacWfbGVubWVzaWRpcl8uCkJpbGluZGnEn2kgw7x6ZXJlIGJ1bmxhciBtb2Rlcm4gcHJvZ3JhbWxhbWFuxLFuIHRlbWVsIHVuc3VybGFyxLFkxLFyLgoKPiBEZW1layBraSwgc2FkZWNlIGJpbGdpc2F5YXJsYXIgYmlsZ2kgZGVwb2xheWFuIHZlIG8gYmlsZ2l5aSBpxZ9sZXllbiBheWfEsXRsYXIgZGXEn2lsZGlyLiBFdnJlbmluIGtlbmRpc2kgZGUsIGJhxZ90YW4gc29uYSBiaWxnaSBpxZ9sZXllbiBkZXZhc2EgYmlyIHNpc3RlbWRpci4gCgoKIyMjIyBWZW7DvHMtc2luZWtrYXBhbiBCaXRraXNpCl9fQmlnaXNhecSxbSDDlnJuZcSfaV9fClZlbsO8cyBiaXRraXNpLCBiZXluaSBvbG1hbWFzxLFuYSByYcSfbWVuLCBzYXnEsW0geWFwxLF5b3IgdmUgxZ9hcnRsYXIgc2HEn2xhbmTEscSfxLFuZGEgaGFyZWtldGUgZ2XDp2l5b3IuCgo8ZGl2IGFsaWduPSJjZW50ZXIiPgo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL083ZVFLU2YwTG1ZP3N0YXJ0PTYwIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgo8L2Rpdj4KCgpfX0JpbGdpIMSwxZ9sZW1fXyAKCiAgKiBEdXJ1bTogQcOnxLFrIHlhIGRhIEthcGFsxLEKICAqIEt1cmFsOiAKICAgICogICAxIGtleiBkb2t1bmR1eXNhIGhhesSxcmxhbgogICAgKiAgIDIwIHNuIGnDp2luZGUgMi4ga2V6IGRva3VuZHV5c2Ega2FwYW4gCgogCiMjIyMgTWVrc2lrYSBEYWxnYXPEsQpfX1p1aHVyIMOWcm5lxJ9pX18gCgo+IFBhcsOnYWxhcmRhIG9sbWF5YW4gYmlyIMWfZXkgKGRhbGdhKSwgYsO8dMO8bmRlIG9ydGF5YSDDp8Sxa8SxeW9yLgoKPGRpdiBhbGlnbj0iY2VudGVyIj4KPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC81cEdTYmlBd1U5cyIgZnJhbWVib3JkZXI9IjAiIGFsbG93ZnVsbHNjcmVlbj48L2lmcmFtZT4KPC9kaXY+CgpfX0JpbGdpIMSwxZ9sZW1fXyAKCiAgKiBEdXJ1bTogQXlha3RhIHlhIGRhIE90dXJtdcWfCiAgKiBLdXJhbDogCiAgICAqICAgWWFrxLFuIGtvbcWfdSBheWFrdGEgaXNlLCBheWHEn2Ega2FsawogICAgKiAgIGF5YWt0YSBpc2UsIGJpciBzw7xyZSBzb25yYSBvdHVyCiAgIAogICAKIyMjIyBFbiBLxLFzYSBZb2x1IEJ1bGFuIEthcsSxbmNhbGFyCl9fWnVodXIgw5ZybmXEn2lfXyAKCj4gUGFyw6dhbGFyZGEgb2xtYXlhbiBiaXIgxZ9leSAoemVrYSksIGLDvHTDvG5kZSBvcnRheWEgw6fEsWvEsXlvci4KCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjsiPjxpbWcgc3JjPSJhbnRzU2hvcnRlc3RQYXRoLmpwZyIgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiPjwvcD4KQnUgcmVzaW0gR29zcyB2ZSBhcmthZGHFn2xhcsSxbsSxbiAxOTg5IHnEsWzEsW5kYWtpIE5hdHVyd2lzc2Vtc2NoYWZ0ZW4gbWFrYWxlc2luZGVuIGFsxLFubcSxxZ90xLFyLgooYSkgRGVuZXkgdGFzYXLEsW3EsQooYikgNCBkYWtpa2Egc29ucmEga8O2cHLDvCDDvHplcmluZGVraSBrYXLEsW5jYWxhcgooYSkgOCBkYWtpa2Egc29ucmEgZW4ga8Sxc2EgeW9sIMO8emVyaW5kZSB5b8SfdW5sYcWfbcSxxZ8ga2FyxLFuY2FsYXIuCgpfX0JpbGdpIMSwxZ9sZW1fXyAKCiAgKiBEdXJ1bTogWWVtZWstQXJhIHlhIGRhIFllbWVrLUJ1bHVuZHUKICAqIEt1cmFsOiAKICAgICogICBZZW1lay1BcmEgaXNlIGtva3VudW4geW/En3VuIG9sZHXEn3UgdGFyYWZhIGdpdAogICAgKiAgIFllbWVrLUJ1bHVuZHUgaXNlIGtva3UgYsSxcmFrCiAgIAoKIyMjIyBFa29zaXN0ZW0KX1/Dlnogw7ZyZ8O8dGxlbmltX18gCgo+IERvxJ9hIGRlxJ9pxZ9lbiBkdXJ1bWEgZ8O2cmUga2VuZGluaSB5ZW5pZGVuIHByb2dyYW1sxLF5b3IuCgo8ZGl2IGFsaWduPSJjZW50ZXIiPgo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL3JDYnlBOXZKR0ZvIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgo8L2Rpdj4KCkVrb3Npc3RlbWkgb2x1xZ90dXJhbiBjYW5sxLFsYXIgYXJhc8SxbmRha2kga2FybWHFn8SxayBiYcSfbGFudMSxbGFyCmJpciDDp8SxcnDEsWRhIGJpbGdpIGnFn2xlbWkgeWF6bWF5xLEgem9ybGHFn3TEsXLEsXlvci4KQnVyYWRhIGlsZ2luw6cgb2xhbiBiaXIgw6dvayBub2t0YSB2YXIsIMWfaW1kaWxpawpoZXIgxZ9leWkgZGnEn2VyIGhlciDFn2V5ZSBvbGFuIGJhxJ9sYW50xLFzxLFuxLEgdnVyZ3VsYW1ha2xhIHlldGluZWxpbS4gClllbGxvd3N0b25lIHBhcmvEsW5hLCBnZXRpcmlsZW4ga3VydGxhciBnZXlpayBuw7xmdXN1bnUgYXphbHTEsXlvciwgb3JtYW4gdmUgYml0a2kgw7ZydMO8c8O8IHllbmlkZW4gY2FubGFuxLF5b3IsIGVyb3p5b24gYXphbMSxeW9yLCBuZWhpciB5YXRha2xhcsSxIGfDvMOnbGVuaXlvciB2ZSBwYXJrxLFuIGZpemlraSBjb8SfcmFmeWFzxLEgZGXEn2nFn2l5b3IuIEJpciBrYcOnIGt1cnR1biBidW51IHlhcGFiaWxlY2XEn2luaSBraW0gdGFobWluIGVkZXJkaT8KCgojIyMjIEVrb3Npc3RlbQo8ZGl2IGFsaWduPSJjZW50ZXIiPgo8aWZyYW1lIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkL1Y0Zl8xX3I4MFJZIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgo8L2Rpdj4KCgoKIyBBbGdvcml0bWEKCkFsZ29yaXRtYWxhciwgYmlsZ2lzYXlhciBiaWxpbWluaW4geWFwxLEgdGHFn8SxZMSxci4gQmVsaXJsaSBiaXIgcHJvYmxlbWluIG5hc8SxbCDDp8O2esO8bGVjZcSfaW5lIGRhaXIgaXpsZW5tZXNpIGdlcmVrZW4gYWTEsW1sYXLEsW4gdMO8bcO8bmUgYWxnb3JpdG1hIGRlcml6LiDDh8O2esO8bSB5b2x1bnUgZMO8xZ/DvG7DvHLDvHosIHZlIGJ1IGTDvMWfw7xuY2VtaXppIGtvZGEgZMO2bsO8xZ90w7xyw7xyw7x6LgoKPiBBbGdvcml0bWFsYXIga29kdW4gacOnZXJpc2luZSBnw7Ztw7xsbcO8xZ8gZMO8xZ/DvG5jZWxlcmRpcgoKVGFyaWYgZWRlYmlsaWRpxJ9pbWl6IGhlciDFn2V5aW4gcHJvZ3JhbcSxbsSxIHlhemFiaWxpcml6LiBCaXIgacWfaSB0YXJpZiBlZGViaWxtZWssIG8gacWfaW4gYWxnb3JpdG1hc8SxbsSxIGJpbG1layBkZW1la3Rpci4gw5ZybmXEn2luLCBtZW5lbWVuIHlhcG1hecSxIGJpbGl5b3JzYW7EsXosIGJhbmEgeWVtZcSfaW4gYWxnb3JpdG1hc8SxbsSxIHlhbmkgdGFyaWZpbmkgdmVyZWJpbGlyc2luaXouCgpCaWxnaXNheWFyIGJpbGltaW5kZWtpIGVuIMO2bmVtbGkgYWxnb3JpdG1hbGFyxLFuIGJhxZ/EsW5kYSBzxLFyYWxhbWEgYWxnb3JpdG1hbGFyxLEgZ2VsaXIuIEdvb2dsZSBzaXpjZSBhcmFtYSBtb3RvcnUgbXVkdXIsIHlva3NhIHPEsXJhbGFtYSBtb3RvcnUgbXVkdXI/CgoKCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBFa2xlLXPEsXJhbGEgYWxnb3JpdG1hc8SxCjEuRMO2bmfDvDogaSA9IDAnZGFuIE4neWUsIGhlciBpIGnDp2luLCAjIGkneWkgRWtsZQogICAgMi5Ew7ZuZ8O8OiBqID0gaSdkZW4gMSdlLCBoZXIgaiBpw6dpbiwgCiAgICAgICAgai5pbmNpIGVsZW1hbiwgYmlyIMO2bmNla2lkZW4ga8O8w6fDvGtzZSwgIyBpJ3lpIFPEsXJhbGEKICAgICAgICAgICAgaWtpc2luaW4geWVyaW5pIGRlxJ9pxZ90aXIKICAgICAgICAgICAgaid5aSBiaXIgYXphbHQKICAgICAgICBkZcSfaWxzZQogICAgICAgICAgICAyLkTDtm5nw7xkZW4gw6fEsWsKICAgIGkneWkgYmlyIGFydHTEsXIKYGBgCll1a2FyxLFkYSBla2xlLXPEsXJhbGEgYWxnb3JpdG1hc8SxbsSxIGHFn2HEn8SxZGEgZ8O2csO8eW9yc3VudXouIMSwbGsgYmFrxLHFn3RhIGthcsSxxZ/EsWsgZ2VsZWJpbGlyIGFtYSBhc2zEsW5kYSBvbGR1a8OnYSBiYXNpdHRpci4gCgo+IMWew7Z5bGUgZMO8xZ/DvG7DvG4sIG1hc2FkYWtpIGRlc3RlZGVuIGthcnQgc2XDp2l5b3JzdW51ei4gxLBsayBrYXJ0xLEgc8SxcmFsYW1heWEgZ2VyZWsgeW9rLiDEsGtpbmNpIGthcnTEsSwgaWxraXlsZSBrYXLFn8SxbGHFn3TEsXLEsXAga8O8w6fDvGtzZSBzb2xhIGtveWFyc8SxbsSxei4gQnUgxZ9la2lsZGUsIG1hc2FkYW4gYWxkxLHEn8SxbsSxIHllbmkga2FydMSxIGVsaW5pemUgX19la2xlX18gcnNpbml6IHZlIHNvbnJhIF9fc8SxcmFsYV9fIHJzxLFuxLF6LiAKCkHFn2HEn8SxZGFraSB2aWRlb2RhLCBmb2xrbG9yIGdydWJ1IGVrbGUtc8SxcmFsYSBhbGdvcml0bWFzxLFuYSBnw7ZyZSBkYW5zIGVkaXlvci4gQnUgdmlkZW95YSBiYWvEsW5jYSwgbmUgZGVtZWsgaXN0ZWRpxJ9pbWkgZGFoYSBpeWkgYW5sYXlhYmlsaXJzaW5pei4KCjxkaXYgYWxpZ249ImNlbnRlciI+CjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvUk9hbFUzNzlsM1U/c3RhcnQ9MTIiIGZyYW1lYm9yZGVyPSIwIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+CjwvZGl2PgoKQcWfYcSfxLFkYWtpIHZpZGVvZGEsIGthcmdhbsSxbiB1eWd1bGFkxLHEn8SxIGFsZ29yaXRtYXnEsSBnw7ZyZWJpbGl5b3IgbXVzdW51ej8KCjxkaXYgYWxpZ249ImNlbnRlciI+CjxpZnJhbWUgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvMTQtLS1VYzRMQ3c/c3RhcnQ9MjAiIGZyYW1lYm9yZGVyPSIwIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+CjwvZGl2PgoKX19BbGdvcnRpbWFfXyAsIHllbWVrIHlhcG1ha3RhbiBkYW5zIGV0bWV5ZSBoZXIgxZ9leSBhbGdvcml0bWFkxLFyLiBCaXIgacWfaW4gbmFzxLFsIGdlcsOnZWtsZcWfdGlyaWxlY2XEn2luaSBhbmxhdGFuIHPEsXJhbMSxIGtvbXV0bGFyLgoKCgojIyBIYXTEsXJsYXRtYWxhciAKICAqIF9fQmlsaW1fXyB5YXBtYW7EsW4gMyB5b2x1CiAgKiBfX0JpbGdpc2F5xLFtX18gLCBzaW5lay1rYXBhbidkYW4ga2FyYSBkZWxpa2xlcmUsIG9yYWRhbiBLLW1lYW5zIGFsZ29yaXRtYXPEsW5hCiAgKiBfX01vZGVsX18gLCDDtm5lbWxpIG9sYW5sYXLEsSB0dXQsIMO2bmVtc2l6IG9sYW5sYXLEsSBnw7ZybWV6ZGVuIGdlbC4gS2FyaWthdMO8ciwgaGFyaXRhbGFyIHZiLi4KICAqIF9fQWxnb3J0aW1hX18gLCB5ZW1layB5YXBtYWt0YW4gZGFucyBldG1leWUgaGVyIMWfZXkgYWxnb3JpdG1hZMSxci4gQmlyIGnFn2luIG5hc8SxbCBnZXLDp2VrbGXFn3RpcmlsZWNlxJ9pbmkgYW5sYXRhbiBzxLFyYWzEsSBrb211dGxhci4KICAqIF9fUHJvZ3JhbWxhbWFfXyBuxLFuIHRlbWVsZWxsZXJpCiAgICAKICAgICogRcSfZXIgLi4gZGXEn2lsc2UgLi4gCiAgICAqIETDtm5nw7xsZXIsIHRla3JhcmxhcgogICAgKiBGb25rc2l5b25sYXIsIGlzbWkgb2xhbiBrw7zDp8O8ayBwcm9ncmFtIHBhcsOnYWxhcsSxCiAgCiAgCiAgCiAgCiAgCiAgCiAgCiMgTmV0bG9nbyB2ZSBLYXJtYcWfxLFrIFNpc3RlbWxlcgoKQcWfYcSfxLFkYWtpIGtvbXV0bGFyxLEgTmV0bG9nb2RhIHlhemFyc2FrIG5lIG9sdXI/CgpgYGB7ciwgZXZhbD1GQUxTRX0KRm9yd2FyZCA1MApSaWdodCA5MApGb3J3YXJkIDUwClJpZ2h0IDkwCkZvcndhcmQgNTAKUmlnaHQgOTAKRm9yd2FyZCA1MApSaWdodCA5MApgYGAgCiAgCgpgYGB7ciwgZXZhbD1GQUxTRX0gIAo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKdG8gc2V0dXAKICBjbGVhci1hbGwgICAgICAgICAgICAgICAgICA7OyBoZXIgc2V5aSBzaWwKICBjcmVhdGUtdHVydGxlcyAxWwogICAgc2V0IGhlYWRpbmcgLTMwICAgICAgICAgIDs7IGlsayB5b251IGJlbGlybGUKICAgIHNldHh5ICg1IC0gbWF4LXB4Y29yKSAwICA7OyBpbGsga29udW0KICBdCmVuZAo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKdG8gY2l6ICAgICAgICAgICAgICAgICAgICAgIDs7IGthcGx1bWJhZ2FsYXJkYW4gcmljYSBldAogIGFzayB0dXJ0bGVzIFtwb2x5Z29uLWNpel0KZW5kCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owp0byBwb2x5Z29uLWNpeiAgICAgICAgICAgICAgICAgOzsgeWVuaSBmb25rc2l5b24gKHByb3NlZHVyKQogICBwZW4tZG93biAgICAgICAgICAgICAgICAgICAgOzsga2FsZW0gY2l6aW0gaWNpbiBnZXJla2xpCiAgIHJlcGVhdCBrZW5hci1zYXlpc2lbICAgICAgICA7OyBEb25ndQogICAgZm9yd2FyZCB1enVubHVrICAgICAgICAgICAgOzsgSWxlcmxlCiAgICByaWdodCAoMzYwIC8ga2VuYXItc2F5aXNpKSA7OyBTYWdhIERvbgogICBdCmVuZApgYGAgCiAgCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjsiPjxpbWcgc3JjPSJwb2x5Z29uLnBuZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+CiAgCiAgCmBgYHtyLCBldmFsPUZBTFNFfSAgCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cmdsb2JhbHMgW3ZlbnVzLnggdmVudXMueV0KOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKdG8gc2V0dXAKICBjYQogIGNydCA1IFsKICAgIHNldCB4Y29yIHJhbmRvbS14Y29yCiAgICBzZXQgeWNvciByYW5kb20teWNvcgogICAgc2V0IHNoYXBlICJidWciCiAgICBzZXQgc2l6ZSAxLjUKICBdCiAgc2V0IHZlbnVzLnggcmFuZG9tLXB4Y29yCiAgc2V0IHZlbnVzLnkgcmFuZG9tLXB5Y29yCiAgdmVudXMtZmx5dHJhcHMgMQogIHJlc2V0LXRpY2tzCmVuZAo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owp0byBnbwogIGFzayB0dXJ0bGVzIFsKICAgIHNldCBoZWFkaW5nIHRvd2FyZHN4eSB2ZW51cy54IHZlbnVzLnkKICAgIHNldCBoZWFkaW5nIChoZWFkaW5nICsgMTUgLSByYW5kb20gMzApCiAgICB3YWl0IDAuMDI1CiAgICBmZCAwLjUKICBdCgogIGxldCBib2Nlay5kb2t1bmR1PyBmYWxzZQogIGFzayB0dXJ0bGVzWwogICAgaWYgZGlzdGFuY2V4eSB2ZW51cy54IHZlbnVzLnkgPCAwLjUgWwogICAgICBzaG93ICJib2NlayBkb2t1bmR1IgogICAgXQogIF0KCiAgdGljawplbmQKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzs7IHZlbnVzLWZsdHJhcCA6IEV0Y2lsIENpY2VrIFNpbXVsYXN2ZW51c3lvbnUKOzsKOzsgPj4gcHJvc2VkdXJsZXIgKGZvbmtzb3ZlbnVzLnlvbmxhcikga29kdW11enUgb3JnYW5pemUgZXRtZW1pemUgdmVudXMueWFyZGltY2kgb2x1cmxhci4KOzsgPj4KOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzsgaXN0ZWRpZ2ltIGthZGFyIGV0Y2lsIGNpY2VrIHlhcmF0YWJpbGl5b3J1bQo7OyBwYXJhbWV0ZXJlIDogdGFuZQp0byB2ZW51cy1mbHl0cmFwcyBbdGFuZV0KICByZXBlYXQgdGFuZVt2ZW51cy1mbHl0cmFwIF0KZW5kCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEV0Y2lsIGNpY2VraW4gbWVya2V6IG5va3Rhc2kgdmVudXMueCB2ZW51cy55CnRvIHZlbnVzLWZseXRyYXAKICB2ZW51cy1hYyB2ZW51cy54IHZlbnVzLnkKICB3YWl0IDAuMTI1ICAgICAgICAgIDs7IDAuMTI1IHNuIGJla2xlCiAgdmVudXMta2FwYSB2ZW51cy54IHZlbnVzLnkKICB3YWl0IDAuMjUgICAgICAgICAgIDs7IDAuMjUgc24gYmVrbGUKICB2ZW51cy1hYyB2ZW51cy54IHZlbnVzLnkKZW5kCjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Cjs7IEJlbGxpIHV6YWtsaWt0YWtpIHBhcnNlbGxlcmkgKHBhdGNoKSB2ZW51cy55ZXNpbCB2ZW51cy55YXAsIG1lcmtleiBraXJtaXppCjs7ID4+IEFzYWdpZGFraSBpa2kga29tdXR1biBzaXJhc2kgZGVnaXJzZSBuZSBvbHVyPwo7OyA+PiBLb2R1biBvcmdhbml6YXN5b251IGNvayBvbmVtbGlkaXIuCnRvIHZlbnVzLWFjIFt4IHldCiAgYXNrIHBhdGNoZXMgd2l0aCBbZGlzdGFuY2V4eSB2ZW51cy54IHZlbnVzLnkgPCAyLjNdIFsgc2V0IHBjb2xvciBncmVlbiBdCiAgYXNrIHBhdGNoIHggeSBbc2V0IHBjb2xvciByZWRdCmVuZAo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owo7OyBCZWxsaSB1emFrbGlrdGFraSBwYXJzZWxsZXJpIChwYXRjaCkgeWVzaWwgeWFwLCBtZXJrZXoga2lybWl6aSBvbHN1bgp0byB2ZW51cy1rYXBhIFt4IHldCiAgOzsgeWVzaWwgb2xhbiB1YyBraXNpbWxhcmksIHRla3JhciBzaXlhaCB5YXBpeW9ydXoKICBhc2sgcGF0Y2hlcyB3aXRoIFtkaXN0YW5jZXh5IHZlbnVzLnggdmVudXMueSA8IDIuM10gWyBzZXQgcGNvbG9yIGJsYWNrIF0KICBhc2sgcGF0Y2hlcyB3aXRoIFtkaXN0YW5jZXh5IHZlbnVzLnggdmVudXMueSA8IDIuMV0gWyBzZXQgcGNvbG9yIGdyZWVuIF0KICBhc2sgcGF0Y2hlcyB3aXRoIFtkaXN0YW5jZXh5IHZlbnVzLnggdmVudXMueSA8IDIuMF0gWyBzZXQgcGNvbG9yIG9yYW5nZSBdCiAgYXNrIHBhdGNoIHggeSBbc2V0IHBjb2xvciByZWRdCmVuZAo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OwpgYGAgCiAgCkRlbmV5aW4gbmUgb2x1eW9yPyBgZ29gIGZvbmtzaXlvbnVuIGnDp2luaSBhxZ9hxJ/EsWRha2kgZ2liaSB5YXrEsW4uCgogICAgCmBgYHtyLCBldmFsPUZBTFNFfSAgCiAgOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKdG8gZ28KICBhc2sgdHVydGxlcyBbCiAgICBzZXQgaGVhZGluZyB0b3dhcmRzeHkgdmVudXMueCB2ZW51cy55CiAgICBzZXQgaGVhZGluZyAoaGVhZGluZyArIDE1IC0gcmFuZG9tIDMwKQogICAgd2FpdCAwLjAyNQogICAgZmQgMC41CiAgXQoKICBsZXQgYm9jZWsuZG9rdW5kdT8gZmFsc2UKICBhc2sgdHVydGxlc1sKICAgIGlmIGRpc3RhbmNleHkgdmVudXMueCB2ZW51cy55IDwgMC41IFsKICAgICAgdmVudXMta2FwYSB2ZW51cy54IHZlbnVzLnkKICAgICAgc2V0IHNpemUgMwogICAgICBzZXQgYm9jZWsuZG9rdW5kdT8gdHJ1ZQogICAgICByZXBlYXQgNFsKICAgICAgICB3YWl0IDAuMQogICAgICAgIHNldCBzaXplIHJhbmRvbSA0CiAgICAgIF0KICAgICAgZGllCiAgICBdCiAgXQoKICBpZiBib2Nlay5kb2t1bmR1PyBbCiAgICB2ZW51cy1rYXBhIHZlbnVzLnggdmVudXMueQogICAgd2FpdCAwLjUgICAgICAgICAgIDs7IDAuMjUgc24gYmVrbGUKICAgIHZlbnVzLWFjIHZlbnVzLnggdmVudXMueQogIF0KICB0aWNrCmVuZApgYGAgCgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0idmVudXMucG5nIiAgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiPjwvcD4KCktvZHUgYmlyYXogZ2VsacWfdGlyZWxpbS4KCmBgYHtyLCBldmFsPUZBTFNFfQo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OwpicmVlZFtzaW5lay1rYXBhbmxhciBzaW5lay1rYXBhbl0KYnJlZWRbc2luZWtsZXIgc2luZWtdCnNpbmVrLWthcGFubGFyLW93bltib2Nlay5kb2t1bmR1P10Kc2luZWtsZXItb3duW2hpel0KOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKdG8gc2V0dXAKICBjYQogIGFzayBwYXRjaGVzIFtzZXQgcGNvbG9yIGJsYWNrXQogIGNyZWF0ZS1zaW5la2xlciBzaW5lay1zYXlpc2kgWwogICAgc2V0IHhjb3IgcmFuZG9tLXhjb3IKICAgIHNldCB5Y29yIHJhbmRvbS15Y29yCiAgICBzZXQgc2hhcGUgImJ1ZyIKCiAgICBzZXQgaGl6IDAuMiArIHJhbmRvbS1mbG9hdCAxCiAgICBzZXQgc2l6ZSAxLjU7OzIgLSAyICogKGhpeiAtIDAuMikgOzsgaGl6IGJ1eXVrbHVrIGlsZSB0ZXJzIG9yYW50aWxpIG9sc3VuCgogICAgaWZlbHNlIHhjb3IgPCAwIFtzZXQgY29sb3IgY3lhbl1bc2V0IGNvbG9yIHZpb2xldF0KICAgIGlmIHhjb3IgPCAoLSBtYXgtcHhjb3IgLyAyKSBbc2V0IGNvbG9yIHNreV0KICAgIGlmIHhjb3IgPiAgKG1heC1weGNvciAvIDIpIFtzZXQgY29sb3IgcGlua10KICBdCgogIGNyZWF0ZS1zaW5lay1rYXBhbmxhciBrYXBhbi1zYXlpc2lbCiAgICBzZXQgeGNvciByb3VuZCByYW5kb20teGNvcgogICAgc2V0IHljb3Igcm91bmQgcmFuZG9tLXljb3IKICAgIHNldCBjb2xvciByZWQKICAgIHNldCBib2Nlay5kb2t1bmR1PyBmYWxzZQogIF0KCiAgYXNrIHNpbmVrLWthcGFubGFyIFsKICAgIHZlbnVzLWFjIHhjb3IgeWNvcgogIF0KICByZXNldC10aWNrcwplbmQKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKdG8gZ28KICBhc2sgc2luZWtsZXIgWwogICAgbGV0IGVuLXlha2luLWthcGFuIG1pbi1vbmUtb2Ygc2luZWsta2FwYW5sYXIgWyBkaXN0YW5jZSBteXNlbGYgXQogICAgc2V0IGhlYWRpbmcgdG93YXJkcyBlbi15YWtpbi1rYXBhbgogICAgc2V0IGhlYWRpbmcgKGhlYWRpbmcgKyAxNSAtIHJhbmRvbSAzMCkKICAgIGZkIGhpegogIF0KCiAgYXNrIHNpbmVrLWthcGFubGFyIFsKICAgIGlmIG5vdCBhbnk/IHNpbmVrbGVyIFtzdG9wXQogICAgbGV0IGVuLXlha2luLXNpbmVrIG1pbi1vbmUtb2Ygc2luZWtsZXIgWyBkaXN0YW5jZSBteXNlbGYgXQogICAgaWZlbHNlIGRpc3RhbmNlIGVuLXlha2luLXNpbmVrIDwgMSBbCiAgICAgIHNldCBib2Nlay5kb2t1bmR1PyB0cnVlCiAgICAgIGFzayBlbi15YWtpbi1zaW5layBbZGllXQogICAgXVtzZXQgYm9jZWsuZG9rdW5kdT8gZmFsc2VdCiAgICBpZmVsc2UgYm9jZWsuZG9rdW5kdT8gW3ZlbnVzLWthcGEgeGNvciB5Y29yXVt2ZW51cy1hYyB4Y29yIHljb3JdCiAgXQoKICBpZiBub3QgYW55PyBzaW5la2xlciBbCiAgICBhc2sgc2luZWsta2FwYW5sYXIgW3ZlbnVzLWFjIHhjb3IgeWNvcl0KICAgIHN0b3BdCiAgdGljawplbmQKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKCgo7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Owo7OyBCZWxsaSB1emFrbGlrdGFraSBwYXJzZWxsZXJpIChwYXRjaCkgdmVudXMueWVzaWwgdmVudXMueWFwLCBtZXJrZXoga2lybWl6aQo7OyA+PiBBc2FnaWRha2kgaWtpIGtvbXV0dW4gc2lyYXNpIGRlZ2lyc2UgbmUgb2x1cj8KOzsgPj4gS29kdW4gb3JnYW5pemFzeW9udSBjb2sgb25lbWxpZGlyLgp0byB2ZW51cy1hYyBbeCB5XQogIGFzayBwYXRjaGVzIHdpdGggW2Rpc3RhbmNleHkgeCB5IDwgMi4zXSBbIHNldCBwY29sb3IgZ3JlZW4gXQogIGFzayBwYXRjaCB4IHkgW3NldCBwY29sb3IgcmVkXQplbmQKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKOzsgQmVsbGkgdXpha2xpa3Rha2kgcGFyc2VsbGVyaSAocGF0Y2gpIHllc2lsIHlhcCwgbWVya2V6IGtpcm1pemkgb2xzdW4KdG8gdmVudXMta2FwYSBbeCB5XQogIDs7IHllc2lsIG9sYW4gdWMga2lzaW1sYXJpLCB0ZWtyYXIgc2l5YWggeWFwaXlvcnV6CiAgYXNrIHBhdGNoZXMgd2l0aCBbZGlzdGFuY2V4eSB4IHkgPCAyLjNdIFsgc2V0IHBjb2xvciBibGFjayBdCiAgYXNrIHBhdGNoZXMgd2l0aCBbZGlzdGFuY2V4eSB4IHkgPCAyLjFdIFsgc2V0IHBjb2xvciBncmVlbiBdCiAgYXNrIHBhdGNoZXMgd2l0aCBbZGlzdGFuY2V4eSB4IHkgPCAxLjhdIFsgc2V0IHBjb2xvciBvcmFuZ2UgXQogIGFzayBwYXRjaCB4IHkgW3NldCBwY29sb3IgcmVkXQplbmQKOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsKYGBgCgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0idmVudXMxLnBuZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+CjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjsiPjxpbWcgc3JjPSJ2ZW51czIucG5nIiAgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiPjwvcD4KPHAgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOyI+PGltZyBzcmM9InZlbnVzMy5wbmciICB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSI+PC9wPgoKCkJhc2l0IGJpciBldMOnaWwgw6dpw6dlayBzaW3DvGxhc3lvbnVudW4gbmFzxLFsIGJpciBrYXJhZGVsaWsgc2ltw7xsYXN5b251bmEgYmVuemVkacSfaW5lIHlhIGRhIG5hc8SxbCBrLW1lYW5zIGdpYmkgYmlyIGvDvG1lbGVtZSBhbGdvcnRpbWFzxLFuYSBiZW56ZWRpxJ9pbmUgZGlra2F0IGVkaW4uCgo+IEHFn8SxcsSxeWEga2HDp21hbWFrIMWfYXJ0xLF5bGEsIGJlbGlybGkgYmlyIGRvemRhIGhheWFsIGfDvGPDvCBvbG1hZGFuIGJpbGltIHlhcMSxbGFtYXouIAoKCgoKIyBSIGRpbGkgdmUgWWFwYXkgw5bEn3Jlbm1leWUgR2lyacWfCgpQcm9ncmFtbGFtYW7EsW4geWFwxLEgdGHFn8SxIGtvbnRyb2wgaWZhZGVsZXJpZGlyLiAKCiAgKiBfX0XEn2VyX18gLi4geWFwIF9fZGXEn2lsc2VfXyAuLiB5YXAgCiAgKiBfX2lmX18gLi4geWFwIF9fZWxzZV9fIC4uIHlhcAogIAogIApgYGB7cn0KYiA8LSAwCmIgPC0gaWZlbHNlKGI9PTAsIDEsIDApCnByaW50KHBhc3RlKCJiID0gIiwgYikpCmBgYAoKCgpSIGRpbGluaW4geWFwxLEgdGHFn8SxIGlzZSB2ZWt0w7ZybGVyZGlyLiAKYGBge3J9CiMgYiB2ZWt0b3J1bmRlIGhhbmdpbGVyaSBiaXIKYiA8LSBjKDEsMSwwLDAsMCkKd2hpY2ggKGIgPT0gMSkKYGBgCgpgYGB7cn0KIyBtYXRyaXMgb2x1dHVyYWxpbQpzYXRpcjEgPC0gYgpzYXRpcjIgPC0gMSAtIGIKCm1hdHJpcyA8LSByYmluZChzYXRpcjEsIHNhdGlyMikgIyBzYXRpcmxhcmkgYmlybGVzdGlyCm1hdHJpcwpgYGAKCgoKT3RvbWFzeW9uLCBzxLFrxLFjxLEgacWfbGVyaW4gdGVrcmFyxLFkxLFyLgoKYGBge3J9CmZvcihpIGluIGIpIHByaW50KGlmZWxzZShpPT0wLCAieWF6aSIsICJ0dXJhIikpCmBgYAoKRm9ua3NpeW9ubGFyLCBrb2R1bnV6dSBvcmdhbml6ZSBldG1lbml6aSBzYcSfbGFyLgoKYGBge3J9CnRvcGxhIDwtIGZ1bmN0aW9uKGEsYil7cmV0dXJuIChhICsgYil9CnRvcGxhKDIsNCkKYGBgCgojIE5ld3RvbiBNYXRlbWF0acSfaSAKCgoKYGBge3J9CnggPSBzZXEoLTEsIDEwLCBieSA9IDAuMSkgICMgeCBEZWdlcmxlcmkKeSA9IHheMiArIDE1ICAgICAgICAgICAgICAgIyB5ID0geF4yICsgMTUgcGFyYWJvbMO8CiMKcGxvdCh4LCB5LCB0eXBlID0gImwiLCBjb2wgPSAxLCB5bGltID0gYygwLDEwMCksCiAgICAgbWFpbiA9IGV4cHJlc3Npb24ocGFzdGUoInkgPSIseF4yLCAiKyAxNSBmb25rc2l5b251IHZlIHg9NSBub2t0YXPEsW5kYWtpIHTDvHJldiIpKSwgeGF4dD0ibiIpCmxpbmVzKHgsIDEwICogeCAtIDEwLCBjb2wgPSAiYmx1ZSIpCiMKYWJsaW5lKHYgPSA1LCBjb2wgPSAiZ3JheSIpCnNlZ21lbnRzKDUsIDQwLCA4LCA0MCwgY29sPSAncmVkJykKc2VnbWVudHMoOCwgNDAsIDgsIDcwLCBjb2w9ICdyZWQnKQpzZWdtZW50cyg4LCA3MCwgOCwgODAsIGNvbD0gJ3JlZCcsIGx3ZCA9IDMpCiMKc2VnbWVudHMoNywgNDAsIDcsIDYwLCBjb2w9ICdyZWQnKQpzZWdtZW50cyg3LCA2MCwgNywgNjQsIGNvbD0gJ3JlZCcsIGx3ZCA9IDMpCnRleHQoNi4xLCA3MywgJ0hhdGEnLCBjb2w9ICdyZWQnKQphcnJvd3MoNi41LCA3MywgNy45NSwgNzQsbGVuZ3RoID0gMC4wOCwgY29sPSAncmVkJykKYXJyb3dzKDYuNSwgNzMsIDYuOTUsIDYxLjUsbGVuZ3RoID0gMC4wOCwgY29sPSAncmVkJykKdGV4dCg2LjUsIDM1LCBleHByZXNzaW9uKHBhc3RlKERlbHRhLCB4KSksIGNvbD0gJ3JlZCcpCnRleHQoOC41LCA2MCwgZXhwcmVzc2lvbihwYXN0ZShEZWx0YSwgeSkpLCBjb2w9ICdyZWQnKQp0ZXh0KDIsIDkwLCBleHByZXNzaW9uKHBhc3RlKCdUZWdldGluIGVnaW1pICcsIERlbHRhLCB4LCAnIGt1Y3VsZHVrY2UnKSksIGNvbD0gJ2JsdWUnKQp0ZXh0KDIuMDgsIDgzLCBleHByZXNzaW9uKHBhc3RlKCdoYXRhIGF6YWxhcmFrICcsIERlbHRhLCB5LCAnLycsIERlbHRhLCB4LCAnIHlha2xhc2lyLiAnKSksIGNvbD0gJ3JlZCcpCmF4aXMoMSwgYXQgPSBzZXEoLTEsIDEwLCBieSA9IDEpLCBsYXM9MikKZ3JpZCgpCmBgYAoKCgoKJHkgPSB4XjIgKyAxNSQgZm9ua3NpeW9udSB5IGxlciBmYXJrxLEgYsO2bMO8IHggbGVyIGZhcmvEsSBnaXR0aWvDp2UsCiR5ID0gNSQgbm9rdGFzxLFuZGFraSB0ZcSfZXRlIGJlbnplbWV5ZSBiYcWfbMSxeW9yLAoKJCQKXGZyYWN7XHRyaWFuZ2xlIHl9e1x0cmlhbmdsZSB4fSA9IFxmcmFje3lfMiAtIHlfMX17eV8yIC0geV8xfQokJAoKCiMjIFTDvHJldgpIZXJoYW5naSBiaXIgbmljZWxpxJ9pbiwKYmVsaXJsaSBiaXIgYW5kYQpuZSBrYWRhciBoxLF6bMSxIGLDvHnDvGTDvMSfw7xuw7wgeWEgZGEga8O8w6fDvGxkw7zEn8O8bsO8IMO2bMOnZXIuCiQkClxmcmFje2R4fXtkdH0KJCQKCgoKPHAgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOyI+PGltZyBzcmM9ImltYWdlMS5qcGciICB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSI+PC9wPgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iaW1hZ2UyLmpwZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+Cgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iaW1hZ2UzLmpwZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+Cgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iaW1hZ2U0LmpwZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+Cgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iaW1hZ2U1LmpwZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+Cgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iaW1hZ2U2LmpwZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+Cgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iaW1hZ2U3LmpwZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+CgoKCgoKCgoKIyMgVGVrcmFybMSxIGZvbmtzaXlvbmxhcgoKJCQgeF97bisxfSA9IGYoeF9uKSQkCgp0ZWtyYXJsaSBmb25rc2l5b251bnUgUiBpbGUgw6dpemVsaW0uCgokJCBzX3tuKzF9ID0gZihzX24pID0gc19uICsgXGZyYWN7MX17Ml57bisxfSB9JCQKCmBgYHtyfQppc2xlbSA8LSBmdW5jdGlvbihzLG4pe3JldHVybiAocyArKDEvMl5uKSl9CgpzIDwtIDEKcHJpbnQocykKCnMgPC0gaXNsZW0ocywxKQpwcmludChzKQoKcyA8LSBpc2xlbShzLDIpCnByaW50KHMpCgpzIDwtIGlzbGVtKHMsMykKcHJpbnQocykKYGBgCgpTw7xyZWtsaSB0ZWsgdGVrIHlhem1hayB5ZXJpbmUsIGJ1bnUgYmlyIGZvciBkb27DvHPDvCBpw6dpbmRlIHlhemFsxLFtLgpgYGB7cn0KdCA9IDI6MTAKcyA9IDEKcHJpbnQocykKCmZvcihpIGluIHQpewogIHMgPC0gaXNsZW0ocyxpKQogIHByaW50KHMpCn0KYGBgCgoKYGBge3J9CnNvbiA9IDIwCnQgPSAxOnNvbgpzID0gcmVwKDEsc29uKQpmb3IoaSBpbiB0Wy1zb25dKXsKICBzW2krMV0gPC0gaXNsZW0oc1tpXSxpKQp9CgpwbG90KHQscyxjb2wgPSAicmVkIiwgdHlwZSA9J2InKQpncmlkKCkKYGBgCgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0ibGltaXQucG5nIiAgd2lkdGg9IjI1MCIgaGVpZ2h0PSIzMTUiPjwvcD4KCgoKCiRlX3tuKzF9ID0gZihlX24pID0gZV9uICsgXGZyYWN7MX17KG4rMSkhfSQgCgpgYGB7cn0KZS5pc2xlbSA8LSBmdW5jdGlvbihlLG4pe3JldHVybiAoZSArKDEvZmFjdG9yaWFsKG4rMSkpKX0KCnNvbiA9IDIwCnQgPSAxOnNvbgplID0gcmVwKDEsc29uKQpmb3IoaSBpbiB0Wy1zb25dKXsKICBlW2krMV0gPC0gZS5pc2xlbShlW2ldLGkpCn0KCnBsb3QodCxlLGNvbCA9ICJ2aW9sZXQiLCB0eXBlID0nYicsIHlsaW0gPSBjKDAsMyksCiAgICAgbWFpbiA9ICIyICBpbGUgMyBhcmFzaW5kYWtpIGJ1IHNheWkgbmVkaXI/IikKZ3JpZCgpCmBgYAoKCgoKCgoKCgoKIyMgRGVyZWNlbGkgSW5pcwoKJGYoeCkkIGZvbmtzaXlvbnVuIG1pbmltdW0gbm9rdGFzaW5pCgokJAp4XzEgPSB4XzEgLSBcYWxwaGEgZicoeCkKJCQKCgoKYGBge3J9CnggPSBzZXEoLTIwLDIwLDAuMikKZiA8LSBmdW5jdGlvbih4KXt4XjIgKyA1fQoKZi50dXJldiA8LSBmdW5jdGlvbih4KXsyKnh9CgpkZXJlY2VsaS5pbmlzIDwtIGZ1bmN0aW9uKHgxID0gMTAsIGFkaW0udXp1bmx1ayA9IDAuMjUpewogIGZvcihpIGluIDE6MjApewogICAgeDEgPC0geDEgLSBhZGltLnV6dW5sdWsgKiBmLnR1cmV2KHgxKQogICAgcHJpbnQoeDEpCiAgfQogIHJldHVybih4MSkKfQoKcGxvdCh4LGYoeCksIHR5cGUgPSAnbCcpCnByaW50KGRlcmVjZWxpLmluaXMoKSkKZ3JpZCgpCmBgYAoKCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjsiPjxpbWcgc3JjPSJHcmFkeWFuLnBuZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKPHAgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOyI+PGltZyBzcmM9ImlyaXMucG5nIiAgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiPjwvcD4KCgoKYGBge3J9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFZlcmkgaGF6aXJsaWdpIDogVWMgdHVyIENpY2VrIHZlcmlzaQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZGF0YV9kZiA8LSBhcy5kYXRhLmZyYW1lKGlyaXMpCiMgaWtpIHR1ciBjaWNla2xlIGlsZ2lsZW5lbGltCnR1ciA8LSBkYXRhX2RmJFNwZWNpZXMgJWluJSBjKCJ2aXJnaW5pY2EiLCAic2V0b3NhIikKZGF0YV9kZiA8LSBkYXRhX2RmW3R1cixdCiMgMTogInZpcmdpbmljYSIgdmUgMDogInNldG9zYSIgb2xzdW4KeSA8LSBpZmVsc2UoZGF0YV9kZiRTcGVjaWVzPT0idmlyZ2luaWNhIiwgMSwgMCkKIyA0IGJveXV0IChvemVsbGlrKSB5ZXJpbmUgMiBib3l1dCBvemVsbGlrIGlsZSBpbGdpbGVuZWxpbSA9PiBjaXppbSBrb2xheWxpZ2kgClggPC0gZGF0YV9kZltjKDEsMyldClggPC0gYXMubWF0cml4KFgpICMgWCA8LSBhcy5tYXRyaXgoWC9tYXgoWCkpCiMgVmVyaW1pemluIGlsayA2IGRlZ2VybGVyaW5lIGdveiBhdGFsaW0KaGVhZChYKQpoZWFkKHkpCgpwbG90KFgsIGNvbCA9IHkgKyAxKQpncmlkKCkKCiMgdGhldGEwIGljaW4gaWxrIGtvbG9uIDEgeWFwaWxkaQpYID0gY2JpbmQocmVwKDEsIGxlbmd0aCh5KSksIFgpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTaW5pZmxhbmRpcm1hOiAKIyAgICAgICAgIENpY2VrICJ2aXJnaW5pY2EiIG1pICJzZXRvc2EiIG1pPwojICAgICAgICAgcGV0YWwtbGVuZ3RoLCBzZXBhbC1sZW5ndGggdmVyaXNpbmUgYmFrYXJhayAKIyAgICAgICAgIGthcmFyIHZlcmVjZWdpegojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmBgYAoKCiMgTW9kZWxpbWl6aSB5YXphbMSxbQokXHNpZ21hKHopID0gXGZyYWN7MX17MSArIGUgXnsten19JCB2ZSAkeF8wID0gMSQsICR4XzEgPSBzZXBhbC5sZW5ndGgkLCAkeF8yID0gcGV0YWwubGVuZ3RoJCBvbG1hayDDvHplcmUsIG1vZGVsaW1peiB5YSBkYSBoaXBvdGV6aW1peiBhxZ9hxJ/EsWRha2kgZ2liaWRpciwKCiQkCmhfXHRoZXRhKHgpID0gXHNpZ21hIChcdGhldGEgXGNkb3QgeCkgPSBcc2lnbWEgKFx0aGV0YV8wIHhfMCArIFx0aGV0YV8xIHhfMSArIFx0aGV0YV8yIHhfMikKJCQKQW1hY8SxbcSxeiBoaXBvdGV6aW1peiAkaF9cdGhldGEkIGlsZSBnZXLDp2VrIMOnxLFrdMSxIG9sYW4gJHkkIGFyYXPEsW5kYWtpIGZhcmvEsSBlbiBheiBpbmRpcmVjZWsgJFx0aGV0YV8wJCwgJFx0aGV0YV8xJCwgJFx0aGV0YV8yJCBkZcSfZXJsZXJpbmkgYnVsbWFrdMSxci4gRGnEn2VyIGJpciBpZmFkZXlsZSwKCiQkCkhhdGEoXHRoZXRhKSA9IFxmcmFjezF9ezJ9XHN1bSAoeSAtIGhfXHRoZXRhKHgpKV4yCiQkCgpCdW51biBpw6dpbiBlxJ9pbSBpbmnFnyB5w7ZudGVtaW5pIGt1bGxhbmFjYcSfxLF6LiBIYXRhbsSxbiB0w7xyZXZpbmkgaGVzYXBsYW1hbcSxeiBnZXJla2l5b3IuIAokJApcZnJhY3tkIEhhdGEoXHRoZXRhKX17ZFx0aGV0YV9pfSA9IFxzdW0gLSAoeSAtIGhfXHRoZXRhKHgpKSBcZnJhY3tkIGhfXHRoZXRhKHgpfXtkXHRoZXRhX2l9CiQkCgpEZW1layBraSwgemluY2lyIGt1cmFsxLEgZ2VyZcSfaSBoaXBvdGV6aW1pbiB0w7xyZXZpbmkgaGVzYXBsYW1hbcSxeiBnZXJla2l5b3IuIAoKClxiZWdpbntlcXVhdGlvbn0gClxiZWdpbntzcGxpdH0KXGZyYWN7ZCBoX1x0aGV0YSh4KX17ZFx0aGV0YV9pfSAmPSYgIFxmcmFje2QgXHNpZ21hIChcdGhldGEgXGNkb3QgeCl9e2RcdGhldGFfaX0gXFwgCiY9JiBcc2lnbWEgKFx0aGV0YSBcY2RvdCB4KSAoMSAtIFxzaWdtYSAoXHRoZXRhIFxjZG90IHgpKQpcZnJhY3tkIChcdGhldGFfMCB4XzAgKyBcdGhldGFfMSB4XzEgKyBcdGhldGFfMiB4XzIpfXtkXHRoZXRhX2l9IFxcIAomPSYgXHNpZ21hIChcdGhldGEgXGNkb3QgeCkgKDEgLSBcc2lnbWEgKFx0aGV0YSBcY2RvdCB4KSkgeF9pXFwKJj0mIGhfXHRoZXRhKHgpICgxIC0gaF9cdGhldGEoeCkpIHhfaVxcClxlbmR7c3BsaXR9ClxlbmR7ZXF1YXRpb259CgpUw7xtIGJ1IG1hdGVtYXRpa3NlbCBkZW5rbGVtbGVyaSBiaXIgYXJheWEgZ2V0aXJpcCwgaGF0YW3EsXrEsW4gdMO8cmV2aW5pIGhlc2FwbGF5YWzEsW0KCiQkClxmcmFje2QgSGF0YShcdGhldGEpfXtkXHRoZXRhX2l9ID0gClxzdW0gKGhfXHRoZXRhKHgpIC0geSkgaF9cdGhldGEoeCkgKDEgLSBoX1x0aGV0YSh4KSkgeF9pCiQkCgpgRGVyZWNlbGkgxLBuacWfYAoKSGF0YSBmb25rc2l5b251biBtaW5pbXVtIG5va3Rhc8SxbsSxIGVsZGUgZXRtZWsgacOnaW4gYWTEsW0gYWTEsW0gYcWfYcSfxLFkYWtpIGdpYmkgaWxlcmxleWVjZcSfaXouCgokJApcdGhldGFfaSA9IFx0aGV0YV9pIC0gXGFscGhhIFxmcmFje2QgSGF0YShcdGhldGEpfXtkXHRoZXRhX2l9CiQkClNpZ21vaWQgZm9ua3NpeW9udW4gZMO8bnlhIGfDtnrDvHlsZSBiaXIgZ8O2cmVsaW0uCmBgYHtyfQpzaWdtb2lkIDwtIGZ1bmN0aW9uKHopewogIHJldHVybiAoMSAvICgxICsgZXhwKC16KSkpCn0KeiA9IC0xMDoxMApwbG90KHosIHNpZ21vaWQoeiksIHR5cGU9ImIiKQpgYGAKCmBgYHtyfQpkb2dydSA8LSBmdW5jdGlvbih4LCB0aCl7CiAgcmV0dXJuKHggJSolIHRoKQp9CmBgYAoKCmBgYHtyfQpoaXBvdGV6IDwtIGZ1bmN0aW9uKHgsIHRoKXsKICB6ID0gZG9ncnUoeCx0aCkKICByZXR1cm4oc2lnbW9pZCh6KSkKfQpgYGAKCgoKCmBgYHtyfQp0aCA9IGMoMC4xLCAwLjEsIDAuMSkKCnogPSBYICUqJSB0aApoID0gc2lnbW9pZCh6KQoKaGF0YSA9IHN1bSgoeSAtIGgpICogICh5IC0gaCkpCgphZGltID0gMC4xICMgYWxmYSBhZGltIHV6dW5sdWd1CmZvcihpIGluIDE6Myl7CiAgZ3JhZHlhbiA9IHN1bSgoaC15KSAqIGggKiAoMS1oKSAqIFhbLGldKQogIHRoW2ldID0gdGhbaV0gLSBhZGltICogZ3JhZHlhbgp9CgpwcmludChoYXRhKQpwcmludCh0aCkKYGBgCgoKYGBge3J9Cml0ZXJhc3lvbiA8LSBmdW5jdGlvbihYID0gWCwgeSA9IHksIHRoLCBhZGltID0gMC41KXsKICB6ID0gWCAlKiUgdGgKICBoID0gc2lnbW9pZCh6KQogIGhhdGEgPSBzdW0oKHkgLSBoKSAqICAoeSAtIGgpKQogIGZvcihpIGluIDE6Myl7CiAgICBncmFkeWFuID0gbWVhbigoaC15KSAqIGggKiAoMS1oKSAqIFhbLGldKQogICAgdGhbaV0gPSB0aFtpXSAtIGFkaW0gKiBncmFkeWFuCiAgfQogIHJldHVybihsaXN0KHRoID0gdGggLCBoYXRhID0gaGF0YSkpCn0KYGBgCgpgYGB7cn0KI0ludGlhbCB0aGV0YQp0aCA8LSByZXAoMCxuY29sKFgpKQoKc2ltdWxhc3lvbiA9IGl0ZXJhc3lvbihYLCB5ICwgdGgpCnRoID0gc2ltdWxhc3lvbiR0aApwcmludChzaW11bGFzeW9uKQpgYGAKCmBgYHtyfQojSW50aWFsIHRoZXRhCnRoIDwtIHJlcCgwLG5jb2woWCkpCgpoYXRhIDwtIGZ1bmN0aW9uKHRoKXsKICB6ID0gWCAlKiUgdGgKICBoID0gc2lnbW9pZCh6KQogIHJldHVybihzdW0oKHkgLSBoKSAqICAoeSAtIGgpKSkKfQoKIyBEZXJpdmUgdGhldGEgdXNpbmcgZ3JhZGllbnQgZGVzY2VudCB1c2luZyBvcHRpbSBmdW5jdGlvbgp0aGV0YV9vcHRpbSA8LSBvcHRpbShwYXI9dGgsZm49aGF0YSkKCiNzZXQgdGhldGEKdGhldGEgPC0gdGhldGFfb3B0aW0kcGFyCgojY29zdCBhdCBvcHRpbWFsIHZhbHVlIG9mIHRoZSB0aGV0YQp0aGV0YV9vcHRpbSRwYXIKYGBgCgoKCgpgYGB7cn0KdGggPSBjKC0xLC0xLDEpCmZvcihpIGluIDE6MTAwMDApewogIHNpbXVsYXN5b24gPSBpdGVyYXN5b24oWCwgeSAsIHRoLCBhZGltID0gNSkKICB0aCA9IHNpbXVsYXN5b24kdGgKfQpwcmludChzaW11bGFzeW9uKQpgYGAKYGBge3J9CiN0aCA9IHRoZXRhX29wdGltJHBhcgpwbG90KFhbLDJdLCBYWywzXSwgY29sID0geSArIDEpCm1vZGVsID0gLTEgKiAodGhbMV0gKyB0aFsyXSAqIFhbLDJdKSAvIHRoWzNdCmxpbmVzKFhbLDJdLG1vZGVsKQpncmlkKCkKYGBgCgoKCiMgU2VudGV0aWsgVmVyaQoKRG9ncnVzYWwgU8SxbsSxZmxhbmTEsXJtYSB5w7ZudGVtaW5pIGt1bGxhbmFyYWssIHNlbnRldGlrIGJpciB2ZXJpeWkgaWtpIHPEsW7EsWZhIGF5xLFyYWzEsW0uCgoKYGBge3J9CgpuID0gNTAKCm1lcmtlejEgPSA1Cmt1bWUxLngxID0gcm5vcm0obiwgbWVhbiA9IG1lcmtlejEsIHNkPTMpCmt1bWUxLngyICA9IHJub3JtKG4sIG1lYW4gPSBtZXJrZXoxLCBzZD0zKQprdW1lMS55ID0gcmVwKDAsbikKCm1lcmtlejIgPSAyMAprdW1lMi54MSA9IHJub3JtKG4sIG1lYW4gPSBtZXJrZXoyLCBzZD0zKQprdW1lMi54MiAgPSBybm9ybShuLCBtZWFuID0gbWVya2V6Miwgc2Q9MykKa3VtZTIueSA9IHJlcCgwLG4pCgpwbG90KGt1bWUxLngxLGt1bWUxLngyLGNvbD0gInJlZCIsIHR5cGUgPSAicCIsIHhsaW0gPSBjKDAsMzApLCB5bGltID0gYygwLDMwKSkKbGluZXMoa3VtZTIueDEsa3VtZTIueDIsY29sPSAiZ3JlZW4iLCB0eXBlID0gInAiKQpncmlkKCkKYGBgCgpgYGB7cn0KVlggPSBjYmluZChyZXAoMSwyKm4pLAogICAgICAgICAgYyhrdW1lMS54MSwga3VtZTIueDEpLAogICAgICAgICAgYyhrdW1lMS54Miwga3VtZTIueDIpKQpWeSA9IGMocmVwKDAsbikscmVwKDEsbikpCiAgCnRoID0gYygtMSwtMSwxKQpmb3IoaSBpbiAxOjEwMDAwKXsKICBzaW11bGFzeW9uID0gaXRlcmFzeW9uKFZYLCBWeSAsIHRoLCBhZGltID0gMSkKICB0aCA9IHNpbXVsYXN5b24kdGgKfQpwcmludChzaW11bGFzeW9uKQpgYGAKCmBgYHtyfQojdGggPSB0aGV0YV9vcHRpbSRwYXIKcGxvdChWWFssMl0sIFZYWywzXSwgY29sID0gVnkgKyAyLCB4bGltID0gYygwLDMwKSwgeWxpbSA9IGMoMCwzMCkpCm1vZGVsID0gLTEgKiAodGhbMV0gKyB0aFsyXSAqIFZYWywyXSkgLyB0aFszXQpsaW5lcyhWWFssMl0sbW9kZWwpCmdyaWQoKQpgYGAKCgoKCgoKIyMgRG9ncnVzYWwgQmFnbGFuaW0KW0RvZ3J1c2FsIEJhZ2xhbmltXShodHRwczovL3V6YXkwMC5naXRodWIuaW8va2FodmUvbm90ZWJvb2tzL0RvZ3J1c2FsQmFnbGFuaW0ubmIuaHRtbCkKCgojIyBFbCB5YXrEsXPEsSBpbGUgeWF6xLFsbcSxxZ8gc2F5xLFsYXLEsSB0YW7EsW1hCgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iRGlnaXRzLnBuZyIgIHdpZHRoPSI1NjAiIGhlaWdodD0iMzE1Ij48L3A+CgpIYXRhbGFyCgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij48aW1nIHNyYz0iTWlzdGFrZXMucG5nIiAgd2lkdGg9IjU2MCIgaGVpZ2h0PSIzMTUiPjwvcD4KCgogCgoKCg==