1.Veri Yapıları

1.1. Giriş

Giriş Notu: Bu kısım Advanced R kitabınının ilk baskısında “Veri Yapıları” ; ikinci baskısında ise “Vektörler” olarak geçen bölümlere ilişkin alınan notları içermektedir.

R’ın temel veri yapıları, boyutlarına(1d, 2d veya nd) ve homojen olmalarına (tüm veriler aynı türden olmalıdır) veya heterojen olmalarına (veriler farklı türlerde olabilir) göre düzenlenebilir. Bu sınıflama, veri analizinde en sık kullanılan beş veri yapısını ortaya çıkarır:

Homojen Heterojen
1 boyutlu Atomik Vektör Liste
2 boyutlu Matris Data Frame
n boyutlu Array(Dizi)

Hemen hemen tüm diğer nesneler bu temeller üzerine inşa edilmiştir. R’nin 0 boyutlu veya skaler tiplere sahip olmadığına dikkat edilmelidir. Skaler olduğu düşünülen sayılar veya stringler aslında bir birim uzunluktaki vektörlerdir.

  • Bir nesne verildiğinde, hangi veri yapılarından oluştuğunu anlamanın en iyi yolu str() kullanmaktır.


1.2. Vektörler

R’daki temel veri yapısı vektördür. Vektörlerin iki çeşidi vardır: atomik vektörler ve listeler. İçerebildikleri veri türleri açısından farklılık gösterirler. Atomik vektörler için tüm verilerin aynı türde olma şartı var iken; listeler farklı türde veriler barındırabilmektedir.

Not: is.vector(), bir nesnenin vektör olup olmadığını test etmez. Bunun yerine, yalnızca nesne, adlardan başka özniteliği olmayan bir vektörse TRUE değerini verir. is.atomic(x) || is.list(x) bir nesnenin gerçekten bir vektör olup olmadığını test etmek için kullanılabilir.


1.2.1. Atomik Vektörler

Dört temel atomik vektör türü vardır: mantıksal (logical), tamsayı (integer), ondalık (double) ve karakter (character). Tamsayı ve ondalık vektörlerin ikisi de sayısal (numeric) vektörler olarak bilinir. Bunlar dışında sık kullanılmayan iki tür daha vardır: karmaşık (complex) ve ham (raw).

Atomik vektörler genellikle c() fonksiyonu ile (combine’nın kısaltmasıdır) ile oluşturulur.

lgl_var <- c(TRUE, FALSE)
int_var <- c(1L, 6L, 10L)
dbl_var <- c(1, 2.5, 4.5)
chr_var <- c("these are", "some strings")

Bir vektörün türünü typeof() ile ve uzunluğunu length() ile belirleyebiliriz.


Bir atomik vektörün tüm elemanları aynı tip olmalıdır, bu yüzden farklı türleri birleştirmeye çalıştığımızda en esnek tipe zorlanacaklartır. En az esnekten en çok esneğe doğru türler şunlardır: mantıksal, tamsayı, ondalık ve karakter.

Örneğin, bir karakter ve bir tamsayıyı birleştirmek, bir karakter verir:

str(c("a", 1))
 chr [1:2] "a" "1"

Rutin olarak korunan iki nitelik vardır:

names: her elemana bir isim veren bir karakter vektörü.

dim: boyutların kısaltması, vektörleri matrislere veya dizilere dönüştürmek için kullanılan bir tamsayı vektörü.

İsimler

Bir vektör üç şekilde adlandırılabilir:

# When creating it: 
x <- c(a = 1, b = 2, c = 3)

# By assigning a character vector to names()
x <- 1:3
names(x) <- c("a", "b", "c")

# Inline, with setNames():
x <- setNames(1:3, c("a", "b", "c"))
Boyutlar

Bir vektöre dim özelliği eklemek, vektörün 2 boyutlu bir matris veya çok boyutlu bir dizi gibi davranmasını sağlar. Matrisler ve diziler, programlama araçları değil, öncelikle matematiksel ve istatistiksel araçlardır.

matrix() ve array() ile veya dim() atama formunu kullanarak matrisler ve diziler oluşturabilirsiniz:

# Two scalar arguments specify row and column sizes
x <- matrix(1:6, nrow = 2, ncol = 3)
x
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
# One vector argument to describe all dimensions
y <- array(1:12, c(2, 3, 2))
y
, , 1

     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

, , 2

     [,1] [,2] [,3]
[1,]    7    9   11
[2,]    8   10   12
# You can also modify an object in place by setting dim()
z <- 1:6
dim(z) <- c(3, 2)
z
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6

Vektörlerle çalışan fonksiyonların çoğunun matrisler ve diziler için de genellemeleri vardır:

Vektör Matris Array
names() rownames(), colnames() dimnames()
length() nrow(), ncol() dim()
c() rbind(), cbind() abind::abind()
t() aperm()
is.null(dim(x)) is.matrix() is.array()
S3 Atomik Vektörler

En önemli vektör özniteliklerinden biri, S3 nesne sisteminin temelini oluşturan class’tır . Bir sınıf özniteliğine sahip olmak, bir nesneyi bir S3 nesnesine dönüştürür; bu, generic bir fonksiyona geçirildiğinde normal bir vektörden farklı davranacağı anlamına gelir. Her S3 nesnesi bir temel türün üzerine inşa edilmiştir ve genellikle ek bilgileri diğer niteliklerde depolar.

Bu bölümde, R tabanında kullanılan dört önemli S3 vektörü tartışılmıştır:

  • Değerlerin faktör vektörlerinde kaydedilen sabit bir seviyeden geldiği kategorik veriler.

  • Tarih vektörlerinde kaydedilen tarihler (gün vb.)

  • POSIXct vektörlerinde depolanan tarih-saatler (saniye veya saniye altı ).

  • difftime vektörlerinde depolanan süreler.


1.2.2. Listeler

Listeler, atomik vektörlerinden farklıdır çünkü içerikleri listeler de dahil olmak üzere herhangi bir türden olabilir. Listeler c() yerine list() kullanarak oluşturulur:

x <- list(1:3, "a", c(TRUE, FALSE, TRUE), c(2.3, 5.9))
str(x)
List of 4
 $ : int [1:3] 1 2 3
 $ : chr "a"
 $ : logi [1:3] TRUE FALSE TRUE
 $ : num [1:2] 2.3 5.9

Listeler bazen özyinelemeli vektörler olarak adlandırılır, çünkü bir liste başka listeler içerebilir. Bu onları temel olarak atomik vektörlerden farklı kılar.

x <- list(list(list(list())))
str(x)
List of 1
 $ :List of 1
  ..$ :List of 1
  .. ..$ : list()
is.recursive(x)
[1] TRUE

1.3. Faktörler

Faktör, yalnızca önceden tanımlanmış değerleri içerebilen bir vektördür. Kategorik verileri depolamak için kullanılır. Faktörler, iki özelliğe sahip bir tamsayı vektörünün üzerine inşa edilir: normal tamsayı vektörlerinden farklı davranmasını sağlayan bir class, ve izin verilen değerler kümesini tanımlayan levels.

x <- list(list(list(list())))
str(x)
List of 1
 $ :List of 1
  ..$ :List of 1
  .. ..$ : list()
is.recursive(x)
[1] TRUE

Faktörler, tümü belirli bir veri kümesinde mevcut olmayan olası değerler kümesi bilindiği durumlarda yararlıdır. Bir karakter vektörünün aksine, bir faktör tablolaştırıldığında, gözlemlenmemiş olanlar dahil tüm kategorilerin sayıları alınır:

sex_char <- c("m", "m", "m")
sex_factor <- factor(sex_char, levels = c("m", "f"))

table(sex_char)
sex_char
m 
3 
table(sex_factor)
sex_factor
m f 
3 0 

Sıralı(ordered) faktörler, faktörlerin küçük bir değişikliğe uğramış versiyonudur. Genel olarak, normal faktörler gibi davranırlar, ancak seviyelerin sırası anlamlıdır (düşük, orta, yüksek) (bazı modelleme ve görselleştirme işlevleri tarafından otomatik olarak kullanılan bir özelliktir).

grade <- ordered(c("b", "b", "a", "c"), levels = c("c", "b", "a"))
grade
[1] b b a c
Levels: c < b < a

Temel R’da faktörlerle çok sık karşılaşılır çünkü birçok temel R fonksiyonu (read.csv() ve data.frame() gibi) karakter vektörleri otomatik olarak faktörlere dönüştürür. Bu, optimal bir durum değildir, çünkü bu fonksiyonların tüm olası düzeyler kümesini veya bunların doğru sırasını bilmelerinin imkanı yoktur (düzeyler, verilerin değil, teorinin veya deneysel tasarımın bir özelliğidir). Bunu önlemek için stringsAsFactors = FALSE argümanı kullanılır ve ardından “teorik” veriler hakkındaki bilgiler kullanılarak karakter vektörler manuel olarak faktörlere dönüştürülür.

Faktörler karakter vektörleri gibi görünse de (ve genellikle böyle davranırlar), tam sayıların üzerine inşa edilirler. Bu yüzden onlara string gibi davranırken dikkatli olunmalıdır. Bazı string yöntemleri (gsub() ve grepl() gibi) faktörleri otomatik olarak stringe zorlar ve bazı fonksiyonların (nchar() gibi) hata vermesine neden olur. Yine de diğerleri (c() gibi) temel tamsayı değerlerini kullanır. Bu nedenle, string benzeri bir türe ihtiyaç olduğu durumlarda, faktörleri karakter vektörlerine dönüştürmek genellikle en iyisidir.

1.4. Matrisler ve Arrays

Bir atomik vektöre dim özelliği eklemek, çok boyutlu bir array gibi davranmasını sağlar. Dizinin (array) özel bir durumu, iki boyutu olan matristir. Matrisler, yaygın olarak istatistik matematiksel mekanizmasının bir parçası olarak kullanılır. Diziler çok daha nadirdir.

Matrisler ve diziler matrix() ve array() ile veya dim() atama biçimi kullanılarak oluşturulur:

######### Bu kısım boyutlar başlığı altında anlatıldı. #########

length() ve names() yüksek boyutlu genellemelere sahiptir:

  • length(), matrisler için nrow() ve ncol()’a ve diziler için dim()’e genelleme yapar.

  • names(), matrisler için rownames() ve colnames()’e ve diziler için karakter vektörlerinin bir listesi olan dimnames()’e genelleme yapar.

```r
a <- matrix(1:6, ncol = 3, nrow = 2)
length(a)
```
```
[1] 6
```
```r
nrow(a)
```
```
[1] 2
```
```r
ncol(a)
```
```
[1] 3
```
```r
rownames(a) <- c("A", "B")
colnames(a) <- c("a", "b", "c")
a
```
```
  a b c
A 1 3 5
B 2 4 6
```
```r
b <- array(1:12, c(2, 3, 2))
length(b)
```
```
[1] 12
```
```r
dim(b)
```
```
[1] 2 3 2
```
```r
dimnames(b) <- list(c("one", "two"), c("a", "b", "c"), c("A", "B"))
b
```
```
, , A

    a b c
one 1 3 5
two 2 4 6

, , B

    a  b  c
one 7  9 11
two 8 10 12
```

c(), matrisler için cbind() ve rbind()’e ve diziler için abind()’e (abindpaketi tarafından sağlanır) genelleştirir. Bir matrisi t(); ile transpoze edebilirsiniz; diziler için genelleştirilmiş eşdeğer aperm()’dir.

Bir nesnenin matris mi yoksa dizi mi olduğunu is.matrix() ve is.array()kullanarak veya dim() uzunluğuna bakarak test edebilirsiniz. as.matrix()ve as.array(), mevcut bir vektörü bir matris veya diziye dönüştürür.

str(1:3)                   # 1d vector
 int [1:3] 1 2 3
str(matrix(1:3, ncol = 1)) # column vector
 int [1:3, 1] 1 2 3
str(matrix(1:3, nrow = 1)) # row vector
 int [1, 1:3] 1 2 3
str(array(1:3, 3))         # "array" vector
 int [1:3(1d)] 1 2 3

1.5. Data Frames

Data frame , R’de veri depolamanın en yaygın yoludur ve sistematik olarak kullanılırsa veri analizini kolaylaştırır. Gerçekte bir data frame, eşit uzunluktaki vektörlerin bir listesidir. Bu onu 2 boyutlu bir yapı haline getirir, böylece hem matrisin hem de listenin özelliklerini paylaşır. Bu, bir data frame’in names(), colnames() ve rownames()’e sahip olduğu anlamına gelir, ancak names() ve colnames() aynı şeydir. Bir data frame’in length()’i, aslında içerdiği listenin uzunluğudur ve dolayısıyla ncol() ile aynıdır; nrow() ise satır sayısını verir.

  • 1d yapısı (bir liste gibi davranır) veya 2d yapısı (matris gibi davranır) gibi bir data frame alt kümelere ayrılabilir.

Girdi olarak adlandırılmış vektörleri alan data.frame() kullanılarak bir veri seti oluşturulur:

df <- data.frame(x = 1:3, y = c("a", "b", "c"))
str(df)
'data.frame':   3 obs. of  2 variables:
 $ x: int  1 2 3
 $ y: chr  "a" "b" "c"

as.data.frame() ile bir nesne data frame dönüşürülebilir:

  • Bir vektör, tek sütunlu bir data frame oluşturacaktır.

  • Bir liste, her öğe için bir sütun oluşturur; hepsi aynı uzunlukta değilse bu bir hatadır.

  • Bir matris, matrisle aynı sayıda sütun ve satır içeren bir data frame oluşturur.

1.5.1. Data Frame Birleştirme

Sütun bazında birleştirirken, satır sayısı eşleşmelidir, ancak satır adları yok sayılır. Satır bazında birleştirirken, sütunların hem sayısı hem de adları eşleşmelidir. Aynı sütunlara sahip olmayan data frameleri birleştirmek için plyr::rbind.fill() kullanın.

Vektörleri cbind() birleştirerek bir data frame oluşturmaya çalışmak yaygın bir hatadır. Bu çalışmaz çünkü argümanlardan biri zaten bir data frame olmadıkça cbind() bir matris oluşturacaktır. Bunun yerine doğrudan data.frame()kullanın:

bad <- data.frame(cbind(a = 1:2, b = c("a", "b")))
str(bad)
'data.frame':   2 obs. of  2 variables:
 $ a: chr  "1" "2"
 $ b: chr  "a" "b"
good <- data.frame(a = 1:2, b = c("a", "b"),
  stringsAsFactors = FALSE)
str(good)
'data.frame':   2 obs. of  2 variables:
 $ a: int  1 2
 $ b: chr  "a" "b"
1.5.2. Özel sütunlar

Bir data frame bir vektör listesi olduğundan, bir data frame’in liste olan bir sütuna sahip olması mümkündür:

df <- data.frame(x = 1:3)
df$y <- list(1:2, 1:3, 1:4)
df

Ancak, data.frame() öğesine bir liste verildiğinde, listenin her bir öğesini kendi sütununa koymaya çalışır, bu nedenle bu başarısız olur:

data.frame(x = 1:3, y = list(1:2, 1:3, 1:4))
Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,  : 
  arguments imply differing number of rows: 2, 3, 4

Bir geçici çözüm, I() öğesini kullanmaktır; bu, data.frame() öğesinin listeyi tek bir birim olarak ele almasına neden olur:

I()girdisine AsIs sınıfını ekler, ancak bu genellikle güvenli bir şekilde yoksayılabilir.

Benzer şekilde, satır sayısı data frame ile eşleştiği sürece, matris veya dizi olan bir data frame’in bir sütununa sahip olmak da mümkündür:

Liste ve dizi sütunları dikkatli kullanılmalıdır: data frame ile çalışan birçok fonksiyon, tüm sütunların atomik vektörler olduğunu varsayar.

LS0tDQp0aXRsZTogIkFkdmFuY2VkIFIgYnkgSGFkbGV5IFdpY2toYW06IERhdGEgU3RydWN0dXJlcyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyAqKjEuVmVyaSBZYXDEsWxhcsSxKioNCg0KIyMjIyAxLjEuIEdpcmnFnw0KDQpHaXJpxZ8gTm90dTogQnUga8Sxc8SxbSAqQWR2YW5jZWQgUioga2l0YWLEsW7EsW7EsW4gaWxrIGJhc2vEsXPEsW5kYSAiVmVyaSBZYXDEsWxhcsSxIiA7IGlraW5jaSBiYXNrxLFzxLFuZGEgaXNlICJWZWt0w7ZybGVyIiBvbGFyYWsgZ2XDp2VuIGLDtmzDvG1sZXJlIGlsacWfa2luIGFsxLFuYW4gbm90bGFyxLEgacOnZXJtZWt0ZWRpci4NCg0KUifEsW4gdGVtZWwgdmVyaSB5YXDEsWxhcsSxLCBib3l1dGxhcsSxbmEoMWQsIDJkIHZleWEgbmQpIHZlIGhvbW9qZW4gb2xtYWxhcsSxbmEgKHTDvG0gdmVyaWxlciBheW7EsSB0w7xyZGVuIG9sbWFsxLFkxLFyKSB2ZXlhIGhldGVyb2plbiBvbG1hbGFyxLFuYSAodmVyaWxlciBmYXJrbMSxIHTDvHJsZXJkZSBvbGFiaWxpcikgZ8O2cmUgZMO8emVubGVuZWJpbGlyLiBCdSBzxLFuxLFmbGFtYSwgdmVyaSBhbmFsaXppbmRlIGVuIHPEsWsga3VsbGFuxLFsYW4gYmXFnyB2ZXJpIHlhcMSxc8SxbsSxIG9ydGF5YSDDp8Sxa2FyxLFyOg0KDQp8ICAgICAgICAgICB8IEhvbW9qZW4gICAgICAgfCBIZXRlcm9qZW4gIHwNCnw6LS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tfA0KfCAxIGJveXV0bHUgfCBBdG9taWsgVmVrdMO2ciB8IExpc3RlICAgICAgfA0KfCAyIGJveXV0bHUgfCBNYXRyaXMgICAgICAgIHwgRGF0YSBGcmFtZSB8DQp8IG4gYm95dXRsdSB8IEFycmF5KERpemkpICAgfCAgICAgICAgICAgIHwNCg0KSGVtZW4gaGVtZW4gdMO8bSBkacSfZXIgbmVzbmVsZXIgYnUgdGVtZWxsZXIgw7x6ZXJpbmUgaW7Fn2EgZWRpbG1pxZ90aXIuIFInbmluIDAgYm95dXRsdSB2ZXlhIHNrYWxlciB0aXBsZXJlIHNhaGlwIG9sbWFkxLHEn8SxbmEgZGlra2F0IGVkaWxtZWxpZGlyLiBTa2FsZXIgb2xkdcSfdSBkw7zFn8O8bsO8bGVuIHNhecSxbGFyIHZleWEgc3RyaW5nbGVyIGFzbMSxbmRhIGJpciBiaXJpbSB1enVubHVrdGFraSB2ZWt0w7ZybGVyZGlyLg0KDQotICAgQmlyIG5lc25lIHZlcmlsZGnEn2luZGUsIGhhbmdpIHZlcmkgeWFwxLFsYXLEsW5kYW4gb2x1xZ90dcSfdW51IGFubGFtYW7EsW4gZW4gaXlpIHlvbHUgYHN0cigpYCBrdWxsYW5tYWt0xLFyLg0KDQogICAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgMS4yLiBWZWt0w7ZybGVyDQoNClInZGFraSB0ZW1lbCB2ZXJpIHlhcMSxc8SxIHZla3TDtnJkw7xyLiBWZWt0w7ZybGVyaW4gaWtpIMOnZcWfaWRpIHZhcmTEsXI6IGF0b21payB2ZWt0w7ZybGVyIHZlIGxpc3RlbGVyLiDEsMOnZXJlYmlsZGlrbGVyaSB2ZXJpIHTDvHJsZXJpIGHDp8Sxc8SxbmRhbiBmYXJrbMSxbMSxayBnw7ZzdGVyaXJsZXIuIEF0b21payB2ZWt0w7ZybGVyIGnDp2luIHTDvG0gdmVyaWxlcmluIGF5bsSxIHTDvHJkZSBvbG1hIMWfYXJ0xLEgdmFyIGlrZW47IGxpc3RlbGVyIGZhcmtsxLEgdMO8cmRlIHZlcmlsZXIgYmFyxLFuZMSxcmFiaWxtZWt0ZWRpci4NCg0KIVtdKGh0dHBzOi8vZDMzd3VicmZraTBsNjguY2xvdWRmcm9udC5uZXQvMmZmM2E2Y2ViZjFiYjgwYWJiMmE4MTRhZTFjZmM2N2IxMjgxNzcxMy9hZTg0OC9kaWFncmFtcy92ZWN0b3JzL3N1bW1hcnktdHJlZS5wbmcpe3dpZHRoPSIyMzYifQ0KDQpOb3Q6IGBpcy52ZWN0b3IoKWAsIGJpciBuZXNuZW5pbiB2ZWt0w7ZyIG9sdXAgb2xtYWTEscSfxLFuxLEgdGVzdCBldG1lei4gQnVudW4geWVyaW5lLCB5YWxuxLF6Y2EgbmVzbmUsIGFkbGFyZGFuIGJhxZ9rYSDDtnpuaXRlbGnEn2kgb2xtYXlhbiBiaXIgdmVrdMO2cnNlIFRSVUUgZGXEn2VyaW5pIHZlcmlyLiBgaXMuYXRvbWljKHgpYCBcfFx8IGBpcy5saXN0KHgpYCBiaXIgbmVzbmVuaW4gZ2Vyw6dla3RlbiBiaXIgdmVrdMO2ciBvbHVwIG9sbWFkxLHEn8SxbsSxIHRlc3QgZXRtZWsgacOnaW4ga3VsbGFuxLFsYWJpbGlyLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIyAxLjIuMS4gQXRvbWlrIFZla3TDtnJsZXINCg0KRMO2cnQgdGVtZWwgYXRvbWlrIHZla3TDtnIgdMO8csO8IHZhcmTEsXI6IG1hbnTEsWtzYWwgKGxvZ2ljYWwpLCB0YW1zYXnEsSAoaW50ZWdlciksIG9uZGFsxLFrIChkb3VibGUpIHZlIGthcmFrdGVyIChjaGFyYWN0ZXIpLiBUYW1zYXnEsSB2ZSBvbmRhbMSxayB2ZWt0w7ZybGVyaW4gaWtpc2kgZGUgc2F5xLFzYWwgKG51bWVyaWMpIHZla3TDtnJsZXIgb2xhcmFrIGJpbGluaXIuIEJ1bmxhciBkxLHFn8SxbmRhIHPEsWsga3VsbGFuxLFsbWF5YW4gaWtpIHTDvHIgZGFoYSB2YXJkxLFyOiBrYXJtYcWfxLFrIChjb21wbGV4KSB2ZSBoYW0gKHJhdykuDQoNCiFbXShodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0L2ViNjczMGI4NDFlMzIyOTJkOWZmMzZiMzNhNTkwZTI0YjYyMjFmNDMvNTcxOTIvZGlhZ3JhbXMvdmVjdG9ycy9zdW1tYXJ5LXRyZWUtYXRvbWljLnBuZyl7d2lkdGg9IjI3NCJ9DQoNCkF0b21payB2ZWt0w7ZybGVyIGdlbmVsbGlrbGUgYGMoKWAgZm9ua3NpeW9udSBpbGUgKGNvbWJpbmUnbsSxbiBrxLFzYWx0bWFzxLFkxLFyKSBpbGUgb2x1xZ90dXJ1bHVyLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpsZ2xfdmFyIDwtIGMoVFJVRSwgRkFMU0UpDQppbnRfdmFyIDwtIGMoMUwsIDZMLCAxMEwpDQpkYmxfdmFyIDwtIGMoMSwgMi41LCA0LjUpDQpjaHJfdmFyIDwtIGMoInRoZXNlIGFyZSIsICJzb21lIHN0cmluZ3MiKQ0KYGBgDQoNCkJpciB2ZWt0w7Zyw7xuIHTDvHLDvG7DvCBgdHlwZW9mKClgIGlsZSB2ZSB1enVubHXEn3VudSBgbGVuZ3RoKClgIGlsZSBiZWxpcmxleWViaWxpcml6Lg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KQmlyIGF0b21payB2ZWt0w7Zyw7xuIHTDvG0gZWxlbWFubGFyxLEgYXluxLEgdGlwIG9sbWFsxLFkxLFyLCBidSB5w7x6ZGVuIGZhcmtsxLEgdMO8cmxlcmkgYmlybGXFn3Rpcm1leWUgw6dhbMSxxZ90xLHEn8SxbcSxemRhIGVuIGVzbmVrIHRpcGUgem9ybGFuYWNha2xhcnTEsXIuIEVuIGF6IGVzbmVrdGVuIGVuIMOnb2sgZXNuZcSfZSBkb8SfcnUgdMO8cmxlciDFn3VubGFyZMSxcjogbWFudMSxa3NhbCwgdGFtc2F5xLEsIG9uZGFsxLFrIHZlIGthcmFrdGVyLg0KDQrDlnJuZcSfaW4sIGJpciBrYXJha3RlciB2ZSBiaXIgdGFtc2F5xLF5xLEgYmlybGXFn3Rpcm1laywgYmlyIGthcmFrdGVyIHZlcmlyOg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpzdHIoYygiYSIsIDEpKQ0KDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNClJ1dGluIG9sYXJhayBrb3J1bmFuIGlraSBuaXRlbGlrIHZhcmTEsXI6DQoNCmBuYW1lc2A6IGhlciBlbGVtYW5hIGJpciBpc2ltIHZlcmVuIGJpciBrYXJha3RlciB2ZWt0w7Zyw7wuDQoNCmBkaW1gOiBib3l1dGxhcsSxbiBrxLFzYWx0bWFzxLEsIHZla3TDtnJsZXJpIG1hdHJpc2xlcmUgdmV5YSBkaXppbGVyZSBkw7Zuw7zFn3TDvHJtZWsgacOnaW4ga3VsbGFuxLFsYW4gYmlyIHRhbXNhecSxIHZla3TDtnLDvC4NCg0KIyMjIyMgKirEsHNpbWxlcioqDQoNCkJpciB2ZWt0w7ZyIMO8w6cgxZ9la2lsZGUgYWRsYW5kxLFyxLFsYWJpbGlyOg0KDQpgYGB7ciBlY2hvPVRSVUV9DQojIFdoZW4gY3JlYXRpbmcgaXQ6IA0KeCA8LSBjKGEgPSAxLCBiID0gMiwgYyA9IDMpDQoNCiMgQnkgYXNzaWduaW5nIGEgY2hhcmFjdGVyIHZlY3RvciB0byBuYW1lcygpDQp4IDwtIDE6Mw0KbmFtZXMoeCkgPC0gYygiYSIsICJiIiwgImMiKQ0KDQojIElubGluZSwgd2l0aCBzZXROYW1lcygpOg0KeCA8LSBzZXROYW1lcygxOjMsIGMoImEiLCAiYiIsICJjIikpDQpgYGANCg0KIyMjIyMgKipCb3l1dGxhcioqDQoNCkJpciB2ZWt0w7ZyZSBgZGltYCDDtnplbGxpxJ9pIGVrbGVtZWssIHZla3TDtnLDvG4gMiBib3l1dGx1IGJpciBtYXRyaXMgdmV5YSDDp29rIGJveXV0bHUgYmlyIGRpemkgZ2liaSBkYXZyYW5tYXPEsW7EsSBzYcSfbGFyLiBNYXRyaXNsZXIgdmUgZGl6aWxlciwgcHJvZ3JhbWxhbWEgYXJhw6dsYXLEsSBkZcSfaWwsIMO2bmNlbGlrbGUgbWF0ZW1hdGlrc2VsIHZlIGlzdGF0aXN0aWtzZWwgYXJhw6dsYXJkxLFyLg0KDQpgbWF0cml4KClgIHZlIGBhcnJheSgpYCBpbGUgdmV5YSBgZGltKClgIGF0YW1hIGZvcm11bnUga3VsbGFuYXJhayBtYXRyaXNsZXIgdmUgZGl6aWxlciBvbHXFn3R1cmFiaWxpcnNpbml6Og0KDQpgYGB7ciBlY2hvPVRSVUV9DQojIFR3byBzY2FsYXIgYXJndW1lbnRzIHNwZWNpZnkgcm93IGFuZCBjb2x1bW4gc2l6ZXMNCnggPC0gbWF0cml4KDE6NiwgbnJvdyA9IDIsIG5jb2wgPSAzKQ0KeA0KDQojIE9uZSB2ZWN0b3IgYXJndW1lbnQgdG8gZGVzY3JpYmUgYWxsIGRpbWVuc2lvbnMNCnkgPC0gYXJyYXkoMToxMiwgYygyLCAzLCAyKSkNCnkNCg0KIyBZb3UgY2FuIGFsc28gbW9kaWZ5IGFuIG9iamVjdCBpbiBwbGFjZSBieSBzZXR0aW5nIGRpbSgpDQp6IDwtIDE6Ng0KZGltKHopIDwtIGMoMywgMikNCnoNCmBgYA0KDQpWZWt0w7ZybGVybGUgw6dhbMSxxZ9hbiBmb25rc2l5b25sYXLEsW4gw6dvxJ91bnVuIG1hdHJpc2xlciB2ZSBkaXppbGVyIGnDp2luIGRlIGdlbmVsbGVtZWxlcmkgdmFyZMSxcjoNCg0KfCBWZWt0w7ZyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBNYXRyaXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEFycmF5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IFtgbmFtZXMoKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvbmFtZXMuaHRtbCkgICAgICAgIHwgW2Byb3duYW1lcygpYF0oaHR0cHM6Ly90aWJibGUudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvcm93bmFtZXMuaHRtbCksIFtgY29sbmFtZXMoKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvY29sbmFtZXMuaHRtbCkgfCBbYGRpbW5hbWVzKClgXShodHRwczovL3JkcnIuaW8vci9iYXNlL2RpbW5hbWVzLmh0bWwpIHwNCnwgW2BsZW5ndGgoKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvbGVuZ3RoLmh0bWwpICAgICAgfCBbYG5yb3coKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvbnJvdy5odG1sKSwgW2BuY29sKClgXShodHRwczovL3JkcnIuaW8vci9iYXNlL25yb3cuaHRtbCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFtgZGltKClgXShodHRwczovL3JkcnIuaW8vci9iYXNlL2RpbS5odG1sKSAgICAgICAgICAgfA0KfCBbYGMoKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvYy5odG1sKSAgICAgICAgICAgICAgICB8IFtgcmJpbmQoKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvY2JpbmQuaHRtbCksIFtgY2JpbmQoKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvY2JpbmQuaHRtbCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYGFiaW5kOjphYmluZCgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC0tLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgW2B0KClgXShodHRwczovL3JkcnIuaW8vci9iYXNlL3QuaHRtbCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBbYGFwZXJtKClgXShodHRwczovL3JkcnIuaW8vci9iYXNlL2FwZXJtLmh0bWwpICAgICAgIHwNCnwgW2Bpcy5udWxsKGRpbSh4KSlgXShodHRwczovL3JkcnIuaW8vci9iYXNlL05VTEwuaHRtbCkgfCBbYGlzLm1hdHJpeCgpYF0oaHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS9tYXRyaXguaHRtbCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFtgaXMuYXJyYXkoKWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvYXJyYXkuaHRtbCkgICAgfA0KDQojIyMjIyBTMyBBdG9taWsgVmVrdMO2cmxlcg0KDQpFbiDDtm5lbWxpIHZla3TDtnIgw7Z6bml0ZWxpa2xlcmluZGVuIGJpcmksIFMzIG5lc25lIHNpc3RlbWluaW4gdGVtZWxpbmkgb2x1xZ90dXJhbiBgY2xhc3NgJ3TEsXIgLiBCaXIgc8SxbsSxZiDDtnpuaXRlbGnEn2luZSBzYWhpcCBvbG1haywgYmlyIG5lc25leWkgYmlyICoqUzMgbmVzbmVzaW5lKiogZMO2bsO8xZ90w7xyw7xyOyBidSwgKipnZW5lcmljKiogYmlyIGZvbmtzaXlvbmEgZ2XDp2lyaWxkacSfaW5kZSBub3JtYWwgYmlyIHZla3TDtnJkZW4gZmFya2zEsSBkYXZyYW5hY2HEn8SxIGFubGFtxLFuYSBnZWxpci4gSGVyIFMzIG5lc25lc2kgYmlyIHRlbWVsIHTDvHLDvG4gw7x6ZXJpbmUgaW7Fn2EgZWRpbG1pxZ90aXIgdmUgZ2VuZWxsaWtsZSBlayBiaWxnaWxlcmkgZGnEn2VyIG5pdGVsaWtsZXJkZSBkZXBvbGFyLg0KDQpCdSBiw7Zsw7xtZGUsIFIgdGFiYW7EsW5kYSBrdWxsYW7EsWxhbiBkw7ZydCDDtm5lbWxpIFMzIHZla3TDtnLDvCB0YXJ0xLHFn8SxbG3EscWfdMSxcjoNCg0KLSAgIERlxJ9lcmxlcmluICoqZmFrdMO2cioqIHZla3TDtnJsZXJpbmRlIGtheWRlZGlsZW4gc2FiaXQgYmlyIHNldml5ZWRlbiBnZWxkacSfaSBrYXRlZ29yaWsgdmVyaWxlci4NCg0KLSAgICoqVGFyaWgqKiB2ZWt0w7ZybGVyaW5kZSBrYXlkZWRpbGVuIHRhcmlobGVyIChnw7xuIHZiLikNCg0KLSAgICoqUE9TSVhjdCoqIHZla3TDtnJsZXJpbmRlIGRlcG9sYW5hbiB0YXJpaC1zYWF0bGVyIChzYW5peWUgdmV5YSBzYW5peWUgYWx0xLEgKS4NCg0KLSAgICoqZGlmZnRpbWUqKiB2ZWt0w7ZybGVyaW5kZSBkZXBvbGFuYW4gc8O8cmVsZXIuDQoNCiAgICAhW10oaHR0cHM6Ly9kMzN3dWJyZmtpMGw2OC5jbG91ZGZyb250Lm5ldC9iYWExOWQwZWJmOWI5Nzk0OWE3YWQyNTliMjlhMWM0YWUwMzFjOGUyLzhlOWI4L2RpYWdyYW1zL3ZlY3RvcnMvc3VtbWFyeS10cmVlLXMzLTEucG5nKXt3aWR0aD0iMzA2In0NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyMgMS4yLjIuIExpc3RlbGVyDQoNCkxpc3RlbGVyLCBhdG9taWsgdmVrdMO2cmxlcmluZGVuIGZhcmtsxLFkxLFyIMOnw7xua8O8IGnDp2VyaWtsZXJpIGxpc3RlbGVyIGRlIGRhaGlsIG9sbWFrIMO8emVyZSBoZXJoYW5naSBiaXIgdMO8cmRlbiBvbGFiaWxpci4gTGlzdGVsZXIgYygpIHllcmluZSBsaXN0KCkga3VsbGFuYXJhayBvbHXFn3R1cnVsdXI6DQoNCmBgYHtyIGVjaG89VFJVRX0NCnggPC0gbGlzdCgxOjMsICJhIiwgYyhUUlVFLCBGQUxTRSwgVFJVRSksIGMoMi4zLCA1LjkpKQ0Kc3RyKHgpDQpgYGANCg0KTGlzdGVsZXIgYmF6ZW4gw7Z6eWluZWxlbWVsaSB2ZWt0w7ZybGVyIG9sYXJhayBhZGxhbmTEsXLEsWzEsXIsIMOnw7xua8O8IGJpciBsaXN0ZSBiYcWfa2EgbGlzdGVsZXIgacOnZXJlYmlsaXIuIEJ1IG9ubGFyxLEgdGVtZWwgb2xhcmFrIGF0b21payB2ZWt0w7ZybGVyZGVuIGZhcmtsxLEga8SxbGFyLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQp4IDwtIGxpc3QobGlzdChsaXN0KGxpc3QoKSkpKQ0Kc3RyKHgpDQoNCmlzLnJlY3Vyc2l2ZSh4KQ0KDQpgYGANCg0KIyMjIyAxLjMuIEZha3TDtnJsZXINCg0KRmFrdMO2ciwgeWFsbsSxemNhIMO2bmNlZGVuIHRhbsSxbWxhbm3EscWfIGRlxJ9lcmxlcmkgacOnZXJlYmlsZW4gYmlyIHZla3TDtnJkw7xyLiBLYXRlZ29yaWsgdmVyaWxlcmkgZGVwb2xhbWFrIGnDp2luIGt1bGxhbsSxbMSxci4gRmFrdMO2cmxlciwgaWtpIMO2emVsbGnEn2Ugc2FoaXAgYmlyIHRhbXNhecSxIHZla3TDtnLDvG7DvG4gw7x6ZXJpbmUgaW7Fn2EgZWRpbGlyOiBub3JtYWwgdGFtc2F5xLEgdmVrdMO2cmxlcmluZGVuIGZhcmtsxLEgZGF2cmFubWFzxLFuxLEgc2HEn2xheWFuIGJpciBgY2xhc3NgLCB2ZSBpemluIHZlcmlsZW4gZGXEn2VybGVyIGvDvG1lc2luaSB0YW7EsW1sYXlhbiBgbGV2ZWxzYC4NCg0KYGBge3IgZWNobz1UUlVFfQ0KeCA8LSBmYWN0b3IoYygiYSIsICJiIiwgImIiLCAiYSIpKQ0KeA0KDQp0eXBlb2YoeCkNCg0KYXR0cmlidXRlcyh4KQ0KDQpgYGANCg0KRmFrdMO2cmxlciwgdMO8bcO8IGJlbGlybGkgYmlyIHZlcmkga8O8bWVzaW5kZSBtZXZjdXQgb2xtYXlhbiBvbGFzxLEgZGXEn2VybGVyIGvDvG1lc2kgYmlsaW5kacSfaSBkdXJ1bWxhcmRhIHlhcmFybMSxZMSxci4gQmlyIGthcmFrdGVyIHZla3TDtnLDvG7DvG4gYWtzaW5lLCBiaXIgZmFrdMO2ciB0YWJsb2xhxZ90xLFyxLFsZMSxxJ/EsW5kYSwgZ8O2emxlbWxlbm1lbWnFnyBvbGFubGFyIGRhaGlsIHTDvG0ga2F0ZWdvcmlsZXJpbiBzYXnEsWxhcsSxIGFsxLFuxLFyOg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpzZXhfY2hhciA8LSBjKCJtIiwgIm0iLCAibSIpDQpzZXhfZmFjdG9yIDwtIGZhY3RvcihzZXhfY2hhciwgbGV2ZWxzID0gYygibSIsICJmIikpDQoNCnRhYmxlKHNleF9jaGFyKQ0KDQp0YWJsZShzZXhfZmFjdG9yKQ0KDQpgYGANCg0KKipTxLFyYWzEsShvcmRlcmVkKSoqIGZha3TDtnJsZXIsIGZha3TDtnJsZXJpbiBrw7zDp8O8ayBiaXIgZGXEn2nFn2lrbGnEn2UgdcSfcmFtxLHFnyB2ZXJzaXlvbnVkdXIuIEdlbmVsIG9sYXJhaywgbm9ybWFsIGZha3TDtnJsZXIgZ2liaSBkYXZyYW7EsXJsYXIsIGFuY2FrIHNldml5ZWxlcmluIHPEsXJhc8SxIGFubGFtbMSxZMSxciAoZMO8xZ/DvGssIG9ydGEsIHnDvGtzZWspIChiYXrEsSBtb2RlbGxlbWUgdmUgZ8O2cnNlbGxlxZ90aXJtZSBpxZ9sZXZsZXJpIHRhcmFmxLFuZGFuIG90b21hdGlrIG9sYXJhayBrdWxsYW7EsWxhbiBiaXIgw7Z6ZWxsaWt0aXIpLg0KDQpgYGB7ciBlY2hvPVRSVUV9DQpncmFkZSA8LSBvcmRlcmVkKGMoImIiLCAiYiIsICJhIiwgImMiKSwgbGV2ZWxzID0gYygiYyIsICJiIiwgImEiKSkNCmdyYWRlDQpgYGANCg0KVGVtZWwgUidkYSBmYWt0w7ZybGVybGUgw6dvayBzxLFrIGthcsWfxLFsYcWfxLFsxLFyIMOnw7xua8O8IGJpcsOnb2sgdGVtZWwgUiBmb25rc2l5b251IChgcmVhZC5jc3YoKWAgdmUgYGRhdGEuZnJhbWUoKWAgZ2liaSkga2FyYWt0ZXIgdmVrdMO2cmxlcmkgb3RvbWF0aWsgb2xhcmFrIGZha3TDtnJsZXJlIGTDtm7DvMWfdMO8csO8ci4gQnUsIG9wdGltYWwgYmlyIGR1cnVtIGRlxJ9pbGRpciwgw6fDvG5rw7wgYnUgZm9ua3NpeW9ubGFyxLFuIHTDvG0gb2xhc8SxIGTDvHpleWxlciBrw7xtZXNpbmkgdmV5YSBidW5sYXLEsW4gZG/En3J1IHPEsXJhc8SxbsSxIGJpbG1lbGVyaW5pbiBpbWthbsSxIHlva3R1ciAoZMO8emV5bGVyLCB2ZXJpbGVyaW4gZGXEn2lsLCB0ZW9yaW5pbiB2ZXlhIGRlbmV5c2VsIHRhc2FyxLFtxLFuIGJpciDDtnplbGxpxJ9pZGlyKS4gQnVudSDDtm5sZW1layBpw6dpbiBgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFYCBhcmfDvG1hbsSxIGt1bGxhbsSxbMSxciB2ZSBhcmTEsW5kYW4gInRlb3JpayIgdmVyaWxlciBoYWtrxLFuZGFraSBiaWxnaWxlciBrdWxsYW7EsWxhcmFrIGthcmFrdGVyIHZla3TDtnJsZXIgbWFudWVsIG9sYXJhayBmYWt0w7ZybGVyZSBkw7Zuw7zFn3TDvHLDvGzDvHIuDQoNCkZha3TDtnJsZXIga2FyYWt0ZXIgdmVrdMO2cmxlcmkgZ2liaSBnw7Zyw7xuc2UgZGUgKHZlIGdlbmVsbGlrbGUgYsO2eWxlIGRhdnJhbsSxcmxhciksIHRhbSBzYXnEsWxhcsSxbiDDvHplcmluZSBpbsWfYSBlZGlsaXJsZXIuIEJ1IHnDvHpkZW4gb25sYXJhIHN0cmluZyBnaWJpIGRhdnJhbsSxcmtlbiBkaWtrYXRsaSBvbHVubWFsxLFkxLFyLiBCYXrEsSBzdHJpbmcgecO2bnRlbWxlcmkgKGBnc3ViKClgIHZlIGBncmVwbCgpYCBnaWJpKSBmYWt0w7ZybGVyaSBvdG9tYXRpayBvbGFyYWsgc3RyaW5nZSB6b3JsYXIgdmUgYmF6xLEgZm9ua3NpeW9ubGFyxLFuIChgbmNoYXIoKWAgZ2liaSkgaGF0YSB2ZXJtZXNpbmUgbmVkZW4gb2x1ci4gWWluZSBkZSBkacSfZXJsZXJpIChgYygpYCBnaWJpKSB0ZW1lbCB0YW1zYXnEsSBkZcSfZXJsZXJpbmkga3VsbGFuxLFyLiBCdSBuZWRlbmxlLCBzdHJpbmcgYmVuemVyaSBiaXIgdMO8cmUgaWh0aXlhw6cgb2xkdcSfdSBkdXJ1bWxhcmRhLCBmYWt0w7ZybGVyaSBrYXJha3RlciB2ZWt0w7ZybGVyaW5lIGTDtm7DvMWfdMO8cm1layBnZW5lbGxpa2xlIGVuIGl5aXNpZGlyLg0KDQojIyMjIDEuNC4gTWF0cmlzbGVyIHZlIEFycmF5cw0KDQpCaXIgYXRvbWlrIHZla3TDtnJlIGBkaW1gIMO2emVsbGnEn2kgZWtsZW1laywgw6dvayBib3l1dGx1IGJpciAqKmFycmF5KiogZ2liaSBkYXZyYW5tYXPEsW7EsSBzYcSfbGFyLiBEaXppbmluIChhcnJheSkgw7Z6ZWwgYmlyIGR1cnVtdSwgaWtpIGJveXV0dSBvbGFuIG1hdHJpc3Rpci4gTWF0cmlzbGVyLCB5YXlnxLFuIG9sYXJhayBpc3RhdGlzdGlrIG1hdGVtYXRpa3NlbCBtZWthbml6bWFzxLFuxLFuIGJpciBwYXLDp2FzxLEgb2xhcmFrIGt1bGxhbsSxbMSxci4gRGl6aWxlciDDp29rIGRhaGEgbmFkaXJkaXIuDQoNCk1hdHJpc2xlciB2ZSBkaXppbGVyICoqbWF0cml4KCkqKiB2ZSAqKmFycmF5KCkqKiBpbGUgdmV5YSAqKmRpbSgpKiogYXRhbWEgYmnDp2ltaSBrdWxsYW7EsWxhcmFrIG9sdcWfdHVydWx1cjoNCg0KKlwjXCNcI1wjXCNcI1wjXCNcIyBCdSBrxLFzxLFtIGJveXV0bGFyIGJhxZ9sxLHEn8SxIGFsdMSxbmRhIGFubGF0xLFsZMSxLiBcI1wjXCNcI1wjXCNcI1wjXCMqDQoNCmBsZW5ndGgoKWAgdmUgYG5hbWVzKClgIHnDvGtzZWsgYm95dXRsdSBnZW5lbGxlbWVsZXJlIHNhaGlwdGlyOg0KDQotICAgYGxlbmd0aCgpYCwgbWF0cmlzbGVyIGnDp2luIGBucm93KClgIHZlIGBuY29sKClgJ2EgdmUgZGl6aWxlciBpw6dpbiBgZGltKClgJ2UgZ2VuZWxsZW1lIHlhcGFyLg0KDQotICAgYG5hbWVzKClgLCBtYXRyaXNsZXIgacOnaW4gYHJvd25hbWVzKClgIHZlIGBjb2xuYW1lcygpYCdlIHZlIGRpemlsZXIgacOnaW4ga2FyYWt0ZXIgdmVrdMO2cmxlcmluaW4gYmlyIGxpc3Rlc2kgb2xhbiBgZGltbmFtZXMoKWAnZSBnZW5lbGxlbWUgeWFwYXIuDQoNCiAgICBgYGB7ciBlY2hvPVRSVUV9DQogICAgYSA8LSBtYXRyaXgoMTo2LCBuY29sID0gMywgbnJvdyA9IDIpDQogICAgbGVuZ3RoKGEpDQogICAgbnJvdyhhKQ0KICAgIG5jb2woYSkNCiAgICByb3duYW1lcyhhKSA8LSBjKCJBIiwgIkIiKQ0KICAgIGNvbG5hbWVzKGEpIDwtIGMoImEiLCAiYiIsICJjIikNCiAgICBhDQoNCiAgICBiIDwtIGFycmF5KDE6MTIsIGMoMiwgMywgMikpDQogICAgbGVuZ3RoKGIpDQogICAgZGltKGIpDQogICAgZGltbmFtZXMoYikgPC0gbGlzdChjKCJvbmUiLCAidHdvIiksIGMoImEiLCAiYiIsICJjIiksIGMoIkEiLCAiQiIpKQ0KICAgIGINCg0KICAgIGBgYA0KDQpgYygpYCwgbWF0cmlzbGVyIGnDp2luIGBjYmluZCgpYCB2ZSBgcmJpbmQoKWAnZSB2ZSBkaXppbGVyIGnDp2luIGBhYmluZCgpYCdlIChgYWJpbmRgcGFrZXRpIHRhcmFmxLFuZGFuIHNhxJ9sYW7EsXIpIGdlbmVsbGXFn3RpcmlyLiBCaXIgbWF0cmlzaSBgdCgpYDsgaWxlIHRyYW5zcG96ZSBlZGViaWxpcnNpbml6OyBkaXppbGVyIGnDp2luIGdlbmVsbGXFn3RpcmlsbWnFnyBlxZ9kZcSfZXIgYGFwZXJtKClgJ2Rpci4NCg0KQmlyIG5lc25lbmluIG1hdHJpcyBtaSB5b2tzYSBkaXppIG1pIG9sZHXEn3VudSBgaXMubWF0cml4KClgIHZlIGBpcy5hcnJheSgpYGt1bGxhbmFyYWsgdmV5YSBgZGltKClgIHV6dW5sdcSfdW5hIGJha2FyYWsgdGVzdCBlZGViaWxpcnNpbml6LiBgYXMubWF0cml4KClgdmUgYGFzLmFycmF5KClgLCBtZXZjdXQgYmlyIHZla3TDtnLDvCBiaXIgbWF0cmlzIHZleWEgZGl6aXllIGTDtm7DvMWfdMO8csO8ci4NCg0KYGBge3IgZWNobz1UUlVFfQ0Kc3RyKDE6MykgICAgICAgICAgICAgICAgICAgIyAxZCB2ZWN0b3INCg0Kc3RyKG1hdHJpeCgxOjMsIG5jb2wgPSAxKSkgIyBjb2x1bW4gdmVjdG9yDQoNCnN0cihtYXRyaXgoMTozLCBucm93ID0gMSkpICMgcm93IHZlY3Rvcg0KDQpzdHIoYXJyYXkoMTozLCAzKSkgICAgICAgICAjICJhcnJheSIgdmVjdG9yDQoNCmBgYA0KDQojIyMjIDEuNS4gKipEYXRhIEZyYW1lcyoqDQoNCioqRGF0YSBmcmFtZSoqICwgUidkZSB2ZXJpIGRlcG9sYW1hbsSxbiBlbiB5YXlnxLFuIHlvbHVkdXIgdmUgc2lzdGVtYXRpayBvbGFyYWsga3VsbGFuxLFsxLFyc2EgdmVyaSBhbmFsaXppbmkga29sYXlsYcWfdMSxcsSxci4gR2Vyw6dla3RlIGJpciAqKmRhdGEgZnJhbWUqKiwgZcWfaXQgdXp1bmx1a3Rha2kgdmVrdMO2cmxlcmluIGJpciBsaXN0ZXNpZGlyLiBCdSBvbnUgMiBib3l1dGx1IGJpciB5YXDEsSBoYWxpbmUgZ2V0aXJpciwgYsO2eWxlY2UgaGVtIG1hdHJpc2luIGhlbSBkZSBsaXN0ZW5pbiDDtnplbGxpa2xlcmluaSBwYXlsYcWfxLFyLiBCdSwgYmlyICoqZGF0YSBmcmFtZSdpbioqIGBuYW1lcygpYCwgYGNvbG5hbWVzKClgIHZlIGByb3duYW1lcygpYCdlIHNhaGlwIG9sZHXEn3UgYW5sYW3EsW5hIGdlbGlyLCBhbmNhayBgbmFtZXMoKWAgdmUgYGNvbG5hbWVzKClgIGF5bsSxIMWfZXlkaXIuIEJpciAqKmRhdGEgZnJhbWUnaW4qKiBgbGVuZ3RoKClgJ2ksIGFzbMSxbmRhIGnDp2VyZGnEn2kgbGlzdGVuaW4gdXp1bmx1xJ91ZHVyIHZlIGRvbGF5xLFzxLF5bGEgYG5jb2woKWAgaWxlIGF5bsSxZMSxcjsgYG5yb3coKWAgaXNlIHNhdMSxciBzYXnEsXPEsW7EsSB2ZXJpci4NCg0KLSAgIDFkIHlhcMSxc8SxIChiaXIgbGlzdGUgZ2liaSBkYXZyYW7EsXIpIHZleWEgMmQgeWFwxLFzxLEgKG1hdHJpcyBnaWJpIGRhdnJhbsSxcikgZ2liaSBiaXIgZGF0YSBmcmFtZSBhbHQga8O8bWVsZXJlIGF5csSxbGFiaWxpci4NCg0KR2lyZGkgb2xhcmFrIGFkbGFuZMSxcsSxbG3EscWfIHZla3TDtnJsZXJpIGFsYW4gYGRhdGEuZnJhbWUoKWAga3VsbGFuxLFsYXJhayBiaXIgdmVyaSBzZXRpIG9sdcWfdHVydWx1cjoNCg0KYGBge3IgZWNobz1UUlVFfQ0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gMTozLCB5ID0gYygiYSIsICJiIiwgImMiKSkNCnN0cihkZikNCmBgYA0KDQpgYXMuZGF0YS5mcmFtZSgpYCBpbGUgYmlyIG5lc25lIGRhdGEgZnJhbWUgZMO2bsO8xZ/DvHLDvGxlYmlsaXI6DQoNCi0gICBCaXIgdmVrdMO2ciwgdGVrIHPDvHR1bmx1IGJpciBkYXRhIGZyYW1lIG9sdcWfdHVyYWNha3TEsXIuDQoNCi0gICBCaXIgbGlzdGUsIGhlciDDtsSfZSBpw6dpbiBiaXIgc8O8dHVuIG9sdcWfdHVydXI7IGhlcHNpIGF5bsSxIHV6dW5sdWt0YSBkZcSfaWxzZSBidSBiaXIgaGF0YWTEsXIuDQoNCi0gICBCaXIgbWF0cmlzLCBtYXRyaXNsZSBheW7EsSBzYXnEsWRhIHPDvHR1biB2ZSBzYXTEsXIgacOnZXJlbiBiaXIgZGF0YSBmcmFtZSBvbHXFn3R1cnVyLg0KDQojIyMjIyAqKjEuNS4xLiBEYXRhIEZyYW1lIEJpcmxlxZ90aXJtZSoqDQoNClPDvHR1biBiYXrEsW5kYSBiaXJsZcWfdGlyaXJrZW4sIHNhdMSxciBzYXnEsXPEsSBlxZ9sZcWfbWVsaWRpciwgYW5jYWsgc2F0xLFyIGFkbGFyxLEgeW9rIHNhecSxbMSxci4gU2F0xLFyIGJhesSxbmRhIGJpcmxlxZ90aXJpcmtlbiwgc8O8dHVubGFyxLFuIGhlbSBzYXnEsXPEsSBoZW0gZGUgYWRsYXLEsSBlxZ9sZcWfbWVsaWRpci4gQXluxLEgc8O8dHVubGFyYSBzYWhpcCBvbG1heWFuIGRhdGEgZnJhbWVsZXJpIGJpcmxlxZ90aXJtZWsgacOnaW4gYHBseXI6OnJiaW5kLmZpbGwoKWAga3VsbGFuxLFuLg0KDQpWZWt0w7ZybGVyaSBgY2JpbmQoKWAgYmlybGXFn3RpcmVyZWsgYmlyIGRhdGEgZnJhbWUgb2x1xZ90dXJtYXlhIMOnYWzEscWfbWFrIHlheWfEsW4gYmlyIGhhdGFkxLFyLiBCdSDDp2FsxLHFn21heiDDp8O8bmvDvCBhcmfDvG1hbmxhcmRhbiBiaXJpIHphdGVuIGJpciBkYXRhIGZyYW1lIG9sbWFkxLFrw6dhIGBjYmluZCgpYCBiaXIgbWF0cmlzIG9sdcWfdHVyYWNha3TEsXIuIEJ1bnVuIHllcmluZSBkb8SfcnVkYW4gYGRhdGEuZnJhbWUoKWBrdWxsYW7EsW46DQoNCmBgYHtyIGVjaG89VFJVRX0NCmJhZCA8LSBkYXRhLmZyYW1lKGNiaW5kKGEgPSAxOjIsIGIgPSBjKCJhIiwgImIiKSkpDQpzdHIoYmFkKQ0KDQpnb29kIDwtIGRhdGEuZnJhbWUoYSA9IDE6MiwgYiA9IGMoImEiLCAiYiIpLA0KICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQpzdHIoZ29vZCkNCmBgYA0KDQojIyMjIyAqKjEuNS4yLioqICoqw5Z6ZWwgc8O8dHVubGFyKioNCg0KQmlyIGRhdGEgZnJhbWUgYmlyIHZla3TDtnIgbGlzdGVzaSBvbGR1xJ91bmRhbiwgYmlyIGRhdGEgZnJhbWUnaW4gbGlzdGUgb2xhbiBiaXIgc8O8dHVuYSBzYWhpcCBvbG1hc8SxIG3DvG1rw7xuZMO8cjoNCg0KYGBge3IgZWNobz1UUlVFfQ0KZGYgPC0gZGF0YS5mcmFtZSh4ID0gMTozKQ0KZGYkeSA8LSBsaXN0KDE6MiwgMTozLCAxOjQpDQpkZg0KYGBgDQoNCkFuY2FrLCBkYXRhLmZyYW1lKCkgw7bEn2VzaW5lIGJpciBsaXN0ZSB2ZXJpbGRpxJ9pbmRlLCBsaXN0ZW5pbiBoZXIgYmlyIMO2xJ9lc2luaSBrZW5kaSBzw7x0dW51bmEga295bWF5YSDDp2FsxLHFn8SxciwgYnUgbmVkZW5sZSBidSBiYcWfYXLEsXPEsXogb2x1cjoNCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPVRSVUV9DQpkYXRhLmZyYW1lKHggPSAxOjMsIHkgPSBsaXN0KDE6MiwgMTozLCAxOjQpKQ0KYGBgDQoNCkJpciBnZcOnaWNpIMOnw7Z6w7xtLCBgSSgpYCDDtsSfZXNpbmkga3VsbGFubWFrdMSxcjsgYnUsIGBkYXRhLmZyYW1lKClgIMO2xJ9lc2luaW4gbGlzdGV5aSB0ZWsgYmlyIGJpcmltIG9sYXJhayBlbGUgYWxtYXPEsW5hIG5lZGVuIG9sdXI6DQoNCmBgYHtyIGVjaG89VFJVRX0NCmRmbCA8LSBkYXRhLmZyYW1lKHggPSAxOjMsIHkgPSBJKGxpc3QoMToyLCAxOjMsIDE6NCkpKQ0Kc3RyKGRmbCkNCg0KZGZsWzIsICJ5Il0NCmBgYA0KDQpgSSgpYGdpcmRpc2luZSBgQXNJc2Agc8SxbsSxZsSxbsSxIGVrbGVyLCBhbmNhayBidSBnZW5lbGxpa2xlIGfDvHZlbmxpIGJpciDFn2VraWxkZSB5b2tzYXnEsWxhYmlsaXIuDQoNCkJlbnplciDFn2VraWxkZSwgc2F0xLFyIHNhecSxc8SxIGRhdGEgZnJhbWUgaWxlIGXFn2xlxZ90acSfaSBzw7xyZWNlLCBtYXRyaXMgdmV5YSBkaXppIG9sYW4gYmlyIGRhdGEgZnJhbWUnaW4gYmlyIHPDvHR1bnVuYSBzYWhpcCBvbG1hayBkYSBtw7xta8O8bmTDvHI6DQoNCmBgYHtyIGVjaG89VFJVRX0NCmRmbSA8LSBkYXRhLmZyYW1lKHggPSAxOjMsIHkgPSBJKG1hdHJpeCgxOjksIG5yb3cgPSAzKSkpDQpzdHIoZGZtKQ0KDQpkZm1bMiwgInkiXQ0KYGBgDQoNCkxpc3RlIHZlIGRpemkgc8O8dHVubGFyxLEgZGlra2F0bGkga3VsbGFuxLFsbWFsxLFkxLFyOiBkYXRhIGZyYW1lIGlsZSDDp2FsxLHFn2FuIGJpcsOnb2sgZm9ua3NpeW9uLCB0w7xtIHPDvHR1bmxhcsSxbiBhdG9taWsgdmVrdMO2cmxlciBvbGR1xJ91bnUgdmFyc2F5YXIuDQo=